<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use client::Api;
use failure::Error;
use states::idle::Idle;
use states::install::Install;
use states::{State, StateChangeImpl, StateMachine};
use std::fs::remove_file;
use update_package::{ObjectStatus, UpdatePackage};
use walkdir::WalkDir;

#[derive(Debug, PartialEq)]
pub struct Download {
    pub update_package: UpdatePackage,
}

create_state_step!(Download => Idle);
create_state_step!(Download => Install(update_package));

impl StateChangeImpl for State<Download> {
    fn to_next_state(self) -> Result<StateMachine, Error> {
        // Prune left over from previous installations
        for entry in WalkDir::new(&self.settings.update.download_dir)
            .follow_links(true)
            .min_depth(1)
            .into_iter()
            .filter_entry(|e| e.file_type().is_file())
            .filter_map(|e| e.ok())
            .filter(|e| {
                !self.state
                    .update_package
                    .objects()
                    .iter()
                    .map(|o| o.sha256sum())
                    .collect::<Vec<_>>()
                    .contains(&e.file_name().to_str().unwrap_or(""))
            }) {
            remove_file(entry.path())?;
        }

        // Prune corrupted files
        for object in self.state
            .update_package
            .filter_objects(&self.settings, &ObjectStatus::Corrupted)
        {
            remove_file(&self.settings.update.download_dir.join(object.sha256sum()))?;
        }

        // Download the missing or incomplete objects
        for object in self.state
            .update_package
            .filter_objects(&self.settings, &ObjectStatus::Missing)
            .into_iter()
            .chain(
                self.state
                    .update_package
                    .filter_objects(&self.settings, &ObjectStatus::Incomplete),
            ) {
            Api::new(&self.settings, &self.runtime_settings, &self.firmware)
                .download_object(&self.state.update_package.package_uid(), object.sha256sum())?;
        }

        if self.state
            .update_package
            .objects()
            .iter()
            .all(|o| o.status(&self.settings.update.download_dir).ok() == Some(ObjectStatus::Ready))
        {
            Ok(StateMachine::Install(self.into()))
        } else {
            bail!("Not all objects are ready for use")
        }
    }
}

#[test]
fn skip_download_if_ready() {
    use super::*;
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use std::fs::create_dir_all;
    use update_package::tests::{create_fake_object, create_fake_settings, get_update_package};

    let settings = create_fake_settings();
    let tmpdir = settings.update.download_dir.clone();
    let _ = create_dir_all(&tmpdir);
    let _ = create_fake_object(&settings);

    let machine = StateMachine::Download(State {
        settings: settings,
        runtime_settings: RuntimeSettings::default(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Download {
            update_package: get_update_package(),
        },
    }).step();

    assert_state!(machine, Install);

    assert_eq!(
        WalkDir::new(&tmpdir)
            .follow_links(true)
            .min_depth(1)
            .into_iter()
            .filter_entry(|e| e.file_type().is_file())
            .filter_map(|e| e.ok())
            .count(),
        1,
        "Number of objects is wrong"
    );
}

#[test]
fn download_objects() {
    use super::*;
    use crypto_hash::{hex_digest, Algorithm};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mockito::mock;
    use std::fs::create_dir_all;
    use std::fs::File;
    use std::io::Read;
    use update_package::tests::{create_fake_settings, get_update_package};

    let settings = create_fake_settings();
    let update_package = get_update_package();
    let sha256sum = "c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646";
    let tmpdir = settings.update.download_dir.clone();
    let _ = create_dir_all(&tmpdir);

    // leftover file to ensure it is removed
    let _ = File::create(&tmpdir.join("leftover-file"));

    let mock = mock(
        "GET",
        format!(
            "/products/{}/packages/{}/objects/{}",
            "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
            &update_package.package_uid(),
            &sha256sum
        ).as_str(),
    ).match_header("Content-Type", "application/json")
        .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
        .with_status(200)
        .with_body("1234567890")
        .create();

    let machine = StateMachine::Download(State {
        settings: settings,
        runtime_settings: RuntimeSettings::default(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Download {
            update_package: update_package,
        },
    }).step();

    mock.assert();

    assert_state!(machine, Install);

    assert_eq!(
        WalkDir::new(&tmpdir)
            .follow_links(true)
            .min_depth(1)
            .into_iter()
            .filter_entry(|e| e.file_type().is_file())
            .filter_map(|e| e.ok())
            .count(),
        1,
        "Failed to remove the corrupted object"
    );

    let mut object_content = String::new();
    let _ = File::open(&tmpdir.join(&sha256sum))
        .unwrap()
        .read_to_string(&mut object_content);

    assert_eq!(
        &hex_digest(Algorithm::SHA256, object_content.as_bytes()),
        &sha256sum,
        "Checksum mismatch"
    );
}
<commit_msg>states: download: Use fs::remove_file as it is clearer<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use client::Api;
use failure::Error;
use states::idle::Idle;
use states::install::Install;
use states::{State, StateChangeImpl, StateMachine};
use std::fs;
use update_package::{ObjectStatus, UpdatePackage};
use walkdir::WalkDir;

#[derive(Debug, PartialEq)]
pub struct Download {
    pub update_package: UpdatePackage,
}

create_state_step!(Download => Idle);
create_state_step!(Download => Install(update_package));

impl StateChangeImpl for State<Download> {
    fn to_next_state(self) -> Result<StateMachine, Error> {
        // Prune left over from previous installations
        for entry in WalkDir::new(&self.settings.update.download_dir)
            .follow_links(true)
            .min_depth(1)
            .into_iter()
            .filter_entry(|e| e.file_type().is_file())
            .filter_map(|e| e.ok())
            .filter(|e| {
                !self.state
                    .update_package
                    .objects()
                    .iter()
                    .map(|o| o.sha256sum())
                    .collect::<Vec<_>>()
                    .contains(&e.file_name().to_str().unwrap_or(""))
            }) {
            fs::remove_file(entry.path())?;
        }

        // Prune corrupted files
        for object in self.state
            .update_package
            .filter_objects(&self.settings, &ObjectStatus::Corrupted)
        {
            fs::remove_file(&self.settings.update.download_dir.join(object.sha256sum()))?;
        }

        // Download the missing or incomplete objects
        for object in self.state
            .update_package
            .filter_objects(&self.settings, &ObjectStatus::Missing)
            .into_iter()
            .chain(
                self.state
                    .update_package
                    .filter_objects(&self.settings, &ObjectStatus::Incomplete),
            ) {
            Api::new(&self.settings, &self.runtime_settings, &self.firmware)
                .download_object(&self.state.update_package.package_uid(), object.sha256sum())?;
        }

        if self.state
            .update_package
            .objects()
            .iter()
            .all(|o| o.status(&self.settings.update.download_dir).ok() == Some(ObjectStatus::Ready))
        {
            Ok(StateMachine::Install(self.into()))
        } else {
            bail!("Not all objects are ready for use")
        }
    }
}

#[test]
fn skip_download_if_ready() {
    use super::*;
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use std::fs::create_dir_all;
    use update_package::tests::{create_fake_object, create_fake_settings, get_update_package};

    let settings = create_fake_settings();
    let tmpdir = settings.update.download_dir.clone();
    let _ = create_dir_all(&tmpdir);
    let _ = create_fake_object(&settings);

    let machine = StateMachine::Download(State {
        settings: settings,
        runtime_settings: RuntimeSettings::default(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Download {
            update_package: get_update_package(),
        },
    }).step();

    assert_state!(machine, Install);

    assert_eq!(
        WalkDir::new(&tmpdir)
            .follow_links(true)
            .min_depth(1)
            .into_iter()
            .filter_entry(|e| e.file_type().is_file())
            .filter_map(|e| e.ok())
            .count(),
        1,
        "Number of objects is wrong"
    );
}

#[test]
fn download_objects() {
    use super::*;
    use crypto_hash::{hex_digest, Algorithm};
    use firmware::tests::{create_fake_metadata, FakeDevice};
    use mockito::mock;
    use std::fs::create_dir_all;
    use std::fs::File;
    use std::io::Read;
    use update_package::tests::{create_fake_settings, get_update_package};

    let settings = create_fake_settings();
    let update_package = get_update_package();
    let sha256sum = "c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646";
    let tmpdir = settings.update.download_dir.clone();
    let _ = create_dir_all(&tmpdir);

    // leftover file to ensure it is removed
    let _ = File::create(&tmpdir.join("leftover-file"));

    let mock = mock(
        "GET",
        format!(
            "/products/{}/packages/{}/objects/{}",
            "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
            &update_package.package_uid(),
            &sha256sum
        ).as_str(),
    ).match_header("Content-Type", "application/json")
        .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
        .with_status(200)
        .with_body("1234567890")
        .create();

    let machine = StateMachine::Download(State {
        settings: settings,
        runtime_settings: RuntimeSettings::default(),
        firmware: Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        applied_package_uid: None,
        state: Download {
            update_package: update_package,
        },
    }).step();

    mock.assert();

    assert_state!(machine, Install);

    assert_eq!(
        WalkDir::new(&tmpdir)
            .follow_links(true)
            .min_depth(1)
            .into_iter()
            .filter_entry(|e| e.file_type().is_file())
            .filter_map(|e| e.ok())
            .count(),
        1,
        "Failed to remove the corrupted object"
    );

    let mut object_content = String::new();
    let _ = File::open(&tmpdir.join(&sha256sum))
        .unwrap()
        .read_to_string(&mut object_content);

    assert_eq!(
        &hex_digest(Algorithm::SHA256, object_content.as_bytes()),
        &sha256sum,
        "Checksum mismatch"
    );
}
<|endoftext|>


<commit_before>use std::convert::{From, Into};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;
use std::hash::Hash;
use std::path::PathBuf;
use std::result::Result;
use std::str::FromStr;

use regex::Regex;

use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;

#[derive(Clone)]
#[derive(Hash)]
#[derive(Eq)]
#[derive(PartialEq)]
pub struct FileID {
    id:         FileHash,
    id_type:    FileIDType,
}

impl FileID {

    pub fn new(id_type: FileIDType, id: FileHash) -> FileID {
        FileID {
            id: id,
            id_type: id_type,
        }
    }

    pub fn get_type(&self) -> FileIDType {
        self.id_type.clone()
    }

    pub fn get_id(&self) -> FileHash {
        self.id.clone()
    }

    pub fn parse(string: &String) -> Option<FileID> {
        // we assume that it is an path
        let regex = Regex::new(r"([:alnum:]*)-([:upper:]*)-([A-Za-z0-9-_]*)\.(.*)").unwrap();
        let s = string.split("/").last().unwrap_or("");

        debug!("Regex build: {:?}", regex);
        debug!("Matching string: '{}'", s);
        regex.captures(s).and_then(|capts| {
            // first one is the whole string, index 1-N are the matches.
            if capts.len() != 5 {
                debug!("Matches, but not expected number of groups");
                return None;
            }
            debug!("Matches: {}", capts.len());

            let modname     = capts.at(1).unwrap();
            let hashname    = capts.at(2).unwrap();
            let mut hash    = capts.at(3).unwrap();

            debug!("Destructure FilePath to ID:");
            debug!("                  FilePath: {:?}", s);
            debug!("               Module Name: {:?}", modname);
            debug!("                 Hash Name: {:?}", hashname);
            debug!("                      Hash: {:?}", hash);

            FileIDType::from_str(hashname).map(|idtype| {
                debug!("ID type = {:?}", idtype);
                Some(FileID {
                    id: FileHash::from(hash),
                    id_type: idtype,
                })
            }).ok()
        }).unwrap_or({
            debug!("Did not match");
            debug!("It is no path, actually. So we assume it is an ID already");
            None
        })
    }

}

impl Debug for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "FileID[{:?}]: {:?}",
               self.id_type,
               self.id);
        Ok(())
    }

}

impl Display for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "FileID[{:?}]: {:?}",
               self.id_type,
               self.id);
        Ok(())
    }

}

impl Into<String> for FileID {

    fn into(self) -> String {
        let typestr : String = self.id_type.into();
        let idstr   : String = self.id.into();
        typestr + "-" + &idstr[..]
    }
}

#[cfg(test)]
mod test {

    use storage::file::id::FileID;
    use storage::file::id_type::FileIDType;

    #[test]
    fn file_id_from_string() {
        setup_logger();

        let s1 = String::from("/home/user/testmodule-UUID-some-id.imag");
        let s2 = String::from("/home/user/testmodule-UUID-some-id.extension.imag");
        let s3 = String::from("/home/user/testmodule-NOHASH-some-id.imag");

        let id1 = FileID::parse(&s1).unwrap();
        let id2 = FileID::parse(&s2).unwrap();
        assert!(FileID::parse(&s3).is_none());

        println!("Id 1 : {:?}", id1);
        println!("Id 2 : {:?}", id2);

        assert_eq!(FileIDType::UUID, id1.get_type());
        assert_eq!(FileIDType::UUID, id2.get_type());

        let h1 : String = id1.get_id().into();
        let h2 : String = id2.get_id().into();

        assert_eq!(String::from("some-id"), h1);
        assert_eq!(String::from("some-id"), h2);

        let f1 : String = id1.into();
        let f2 : String = id2.into();

        assert_eq!(String::from("UUID-some-id"), f1);
        assert_eq!(String::from("UUID-some-id"), f2);
    }

    fn setup_logger() {
        extern crate log;
        use log::{LogLevelFilter, set_logger};
        use runtime::ImagLogger;

        log::set_logger(|max_log_lvl| {
            let lvl = LogLevelFilter::Debug;
            max_log_lvl.set(lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        });
        debug!("Init logger for test");
    }

}

<commit_msg>Add docs to FileID<commit_after>use std::convert::{From, Into};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;
use std::hash::Hash;
use std::path::PathBuf;
use std::result::Result;
use std::str::FromStr;

use regex::Regex;

use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;

#[derive(Clone)]
#[derive(Hash)]
#[derive(Eq)]
#[derive(PartialEq)]
/**
 * FileID
 *
 * A FileID contains of two parts: The ID type and the Hash. For example the FileID
 *
 *  UUID-235-1215-1212
 *
 * has a type ("UUID") and a Hash ("235-1215-1212").
 */
pub struct FileID {
    id:         FileHash,
    id_type:    FileIDType,
}

impl FileID {

    pub fn new(id_type: FileIDType, id: FileHash) -> FileID {
        FileID {
            id: id,
            id_type: id_type,
        }
    }

    /**
     * Get the type of the FileID
     */
    pub fn get_type(&self) -> FileIDType {
        self.id_type.clone()
    }

    /**
     * Get the Hash of the FileID
     */
    pub fn get_id(&self) -> FileHash {
        self.id.clone()
    }

    /**
     * Parse a String into a FileID, if possible
     */
    pub fn parse(string: &String) -> Option<FileID> {
        // we assume that it is an path
        let regex = Regex::new(r"([:alnum:]*)-([:upper:]*)-([A-Za-z0-9-_]*)\.(.*)").unwrap();
        let s = string.split("/").last().unwrap_or("");

        debug!("Regex build: {:?}", regex);
        debug!("Matching string: '{}'", s);
        regex.captures(s).and_then(|capts| {
            // first one is the whole string, index 1-N are the matches.
            if capts.len() != 5 {
                debug!("Matches, but not expected number of groups");
                return None;
            }
            debug!("Matches: {}", capts.len());

            let modname     = capts.at(1).unwrap();
            let hashname    = capts.at(2).unwrap();
            let mut hash    = capts.at(3).unwrap();

            debug!("Destructure FilePath to ID:");
            debug!("                  FilePath: {:?}", s);
            debug!("               Module Name: {:?}", modname);
            debug!("                 Hash Name: {:?}", hashname);
            debug!("                      Hash: {:?}", hash);

            FileIDType::from_str(hashname).map(|idtype| {
                debug!("ID type = {:?}", idtype);
                Some(FileID {
                    id: FileHash::from(hash),
                    id_type: idtype,
                })
            }).ok()
        }).unwrap_or({
            debug!("Did not match");
            debug!("It is no path, actually. So we assume it is an ID already");
            None
        })
    }

}

impl Debug for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "FileID[{:?}]: {:?}",
               self.id_type,
               self.id);
        Ok(())
    }

}

impl Display for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "FileID[{:?}]: {:?}",
               self.id_type,
               self.id);
        Ok(())
    }

}

impl Into<String> for FileID {

    fn into(self) -> String {
        let typestr : String = self.id_type.into();
        let idstr   : String = self.id.into();
        typestr + "-" + &idstr[..]
    }
}

#[cfg(test)]
mod test {

    use storage::file::id::FileID;
    use storage::file::id_type::FileIDType;

    #[test]
    fn file_id_from_string() {
        setup_logger();

        let s1 = String::from("/home/user/testmodule-UUID-some-id.imag");
        let s2 = String::from("/home/user/testmodule-UUID-some-id.extension.imag");
        let s3 = String::from("/home/user/testmodule-NOHASH-some-id.imag");

        let id1 = FileID::parse(&s1).unwrap();
        let id2 = FileID::parse(&s2).unwrap();
        assert!(FileID::parse(&s3).is_none());

        println!("Id 1 : {:?}", id1);
        println!("Id 2 : {:?}", id2);

        assert_eq!(FileIDType::UUID, id1.get_type());
        assert_eq!(FileIDType::UUID, id2.get_type());

        let h1 : String = id1.get_id().into();
        let h2 : String = id2.get_id().into();

        assert_eq!(String::from("some-id"), h1);
        assert_eq!(String::from("some-id"), h2);

        let f1 : String = id1.into();
        let f2 : String = id2.into();

        assert_eq!(String::from("UUID-some-id"), f1);
        assert_eq!(String::from("UUID-some-id"), f2);
    }

    fn setup_logger() {
        extern crate log;
        use log::{LogLevelFilter, set_logger};
        use runtime::ImagLogger;

        log::set_logger(|max_log_lvl| {
            let lvl = LogLevelFilter::Debug;
            max_log_lvl.set(lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        });
        debug!("Init logger for test");
    }

}

<|endoftext|>


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

macro_rules! box_database {
    ($($boxenum:ident $boxtype:expr),*,) => {
        #[derive(Clone, Copy, PartialEq)]
        pub enum BoxType {
            $($boxenum),*,
            UnknownBox(u32),
        }

        impl From<u32> for BoxType {
            fn from(t: u32) -> BoxType {
                use self::BoxType::*;
                match t {
                    $($boxtype => $boxenum),*,
                    _ => UnknownBox(t),
                }
            }
        }

        impl Into<u32> for BoxType {
            fn into(self) -> u32 {
                use self::BoxType::*;
                match self {
                    $($boxenum => $boxtype),*,
                    UnknownBox(t) => t,
                }
            }
        }

        impl fmt::Debug for BoxType {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                let fourcc: FourCC = From::from(self.clone());
                write!(f, "{}", fourcc)
            }
        }
    }
}

#[derive(Default, PartialEq, Clone)]
pub struct FourCC {
    pub value: String
}

impl From<u32> for FourCC {
    fn from(number: u32) -> FourCC {
        let mut box_chars = Vec::new();
        for x in 0..4 {
            let c = (number >> (x * 8) & 0x000000FF) as u8;
            box_chars.push(c);
        }
        box_chars.reverse();

        let box_string = match String::from_utf8(box_chars) {
            Ok(t) => t,
            _ => String::from("null"), // error to retrieve fourcc
        };

        FourCC {
            value: box_string
        }
    }
}

impl From<BoxType> for FourCC {
    fn from(t: BoxType) -> FourCC {
        let box_num: u32 = Into::into(t);
        From::from(box_num)
    }
}

impl<'a> From<&'a str> for FourCC {
    fn from(v: &'a str) -> FourCC {
        FourCC {
            value: v.to_owned()
        }
    }
}

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

impl fmt::Display for FourCC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.value)
    }
}

box_database!(
    FileTypeBox                       0x66747970, // "ftyp"
    MovieBox                          0x6d6f6f76, // "moov"
    MovieHeaderBox                    0x6d766864, // "mvhd"
    TrackBox                          0x7472616b, // "trak"
    TrackHeaderBox                    0x746b6864, // "tkhd"
    EditBox                           0x65647473, // "edts"
    MediaBox                          0x6d646961, // "mdia"
    EditListBox                       0x656c7374, // "elst"
    MediaHeaderBox                    0x6d646864, // "mdhd"
    HandlerBox                        0x68646c72, // "hdlr"
    MediaInformationBox               0x6d696e66, // "minf"
    SampleTableBox                    0x7374626c, // "stbl"
    SampleDescriptionBox              0x73747364, // "stsd"
    TimeToSampleBox                   0x73747473, // "stts"
    SampleToChunkBox                  0x73747363, // "stsc"
    SampleSizeBox                     0x7374737a, // "stsz"
    ChunkOffsetBox                    0x7374636f, // "stco"
    ChunkLargeOffsetBox               0x636f3634, // "co64"
    SyncSampleBox                     0x73747373, // "stss"
    AVCSampleEntry                    0x61766331, // "avc1"
    AVC3SampleEntry                   0x61766333, // "avc3" - Need to check official name in spec.
    AVCConfigurationBox               0x61766343, // "avcC"
    MP4AudioSampleEntry               0x6d703461, // "mp4a"
    MP4VideoSampleEntry               0x6d703476, // "mp4v"
    ESDBox                            0x65736473, // "esds"
    VP8SampleEntry                    0x76703038, // "vp08"
    VP9SampleEntry                    0x76703039, // "vp09"
    VPCodecConfigurationBox           0x76706343, // "vpcC"
    AV1SampleEntry                    0x61763031, // "av01"
    AV1CodecConfigurationBox          0x61763143, // "av1C"
    FLACSampleEntry                   0x664c6143, // "fLaC"
    FLACSpecificBox                   0x64664c61, // "dfLa"
    OpusSampleEntry                   0x4f707573, // "Opus"
    OpusSpecificBox                   0x644f7073, // "dOps"
    ProtectedVisualSampleEntry        0x656e6376, // "encv" - Need to check official name in spec.
    ProtectedAudioSampleEntry         0x656e6361, // "enca" - Need to check official name in spec.
    MovieExtendsBox                   0x6d766578, // "mvex"
    MovieExtendsHeaderBox             0x6d656864, // "mehd"
    QTWaveAtom                        0x77617665, // "wave" - quicktime atom
    ProtectionSystemSpecificHeaderBox 0x70737368, // "pssh"
    SchemeInformationBox              0x73636869, // "schi"
    TrackEncryptionBox                0x74656e63, // "tenc"
    ProtectionSchemeInformationBox    0x73696e66, // "sinf"
    OriginalFormatBox                 0x66726d61, // "frma"
    SchemeTypeBox                     0x7363686d, // "schm"
    MP3AudioSampleEntry               0x2e6d7033, // ".mp3" - from F4V.
    CompositionOffsetBox              0x63747473, // "ctts"
    LPCMAudioSampleEntry              0x6C70636D, // "lpcm" - quicktime atom
    ALACSpecificBox                   0x616C6163, // "alac" - Also used by ALACSampleEntry
    UuidBox                           0x75756964, // "uuid"
    MetadataBox                       0x6d657461, // "meta" - IS this needed?
    MetadataHeaderBox                 0x6d686472, // "mhdr"
    MetadataItemKeysBox               0x6b657973, // "keys"
    MetadataItemListEntry             0x696c7374, // "ilst"
    MetadataItemDataEntry             0x64617461, // "data"
    MetadataItemNameBox               0x6e616d65, // "name"
    MetadataItemInformationBox        0x69746966, // "itif"
    UserdataBox                       0x75647461, // "udta"
    TrackNumberBox                    0x74726b6e, // "trkn
    AlbumNameBox                      0xa9616c62, // "©alb"
);
<commit_msg>Define supported iTunes metadata boxes<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::fmt;

macro_rules! box_database {
    ($($boxenum:ident $boxtype:expr),*,) => {
        #[derive(Clone, Copy, PartialEq)]
        pub enum BoxType {
            $($boxenum),*,
            UnknownBox(u32),
        }

        impl From<u32> for BoxType {
            fn from(t: u32) -> BoxType {
                use self::BoxType::*;
                match t {
                    $($boxtype => $boxenum),*,
                    _ => UnknownBox(t),
                }
            }
        }

        impl Into<u32> for BoxType {
            fn into(self) -> u32 {
                use self::BoxType::*;
                match self {
                    $($boxenum => $boxtype),*,
                    UnknownBox(t) => t,
                }
            }
        }

        impl fmt::Debug for BoxType {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                let fourcc: FourCC = From::from(self.clone());
                write!(f, "{}", fourcc)
            }
        }
    }
}

#[derive(Default, PartialEq, Clone)]
pub struct FourCC {
    pub value: String
}

impl From<u32> for FourCC {
    fn from(number: u32) -> FourCC {
        let mut box_chars = Vec::new();
        for x in 0..4 {
            let c = (number >> (x * 8) & 0x000000FF) as u8;
            box_chars.push(c);
        }
        box_chars.reverse();

        let box_string = match String::from_utf8(box_chars) {
            Ok(t) => t,
            _ => String::from("null"), // error to retrieve fourcc
        };

        FourCC {
            value: box_string
        }
    }
}

impl From<BoxType> for FourCC {
    fn from(t: BoxType) -> FourCC {
        let box_num: u32 = Into::into(t);
        From::from(box_num)
    }
}

impl<'a> From<&'a str> for FourCC {
    fn from(v: &'a str) -> FourCC {
        FourCC {
            value: v.to_owned()
        }
    }
}

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

impl fmt::Display for FourCC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.value)
    }
}

box_database!(
    FileTypeBox                       0x66747970, // "ftyp"
    MovieBox                          0x6d6f6f76, // "moov"
    MovieHeaderBox                    0x6d766864, // "mvhd"
    TrackBox                          0x7472616b, // "trak"
    TrackHeaderBox                    0x746b6864, // "tkhd"
    EditBox                           0x65647473, // "edts"
    MediaBox                          0x6d646961, // "mdia"
    EditListBox                       0x656c7374, // "elst"
    MediaHeaderBox                    0x6d646864, // "mdhd"
    HandlerBox                        0x68646c72, // "hdlr"
    MediaInformationBox               0x6d696e66, // "minf"
    SampleTableBox                    0x7374626c, // "stbl"
    SampleDescriptionBox              0x73747364, // "stsd"
    TimeToSampleBox                   0x73747473, // "stts"
    SampleToChunkBox                  0x73747363, // "stsc"
    SampleSizeBox                     0x7374737a, // "stsz"
    ChunkOffsetBox                    0x7374636f, // "stco"
    ChunkLargeOffsetBox               0x636f3634, // "co64"
    SyncSampleBox                     0x73747373, // "stss"
    AVCSampleEntry                    0x61766331, // "avc1"
    AVC3SampleEntry                   0x61766333, // "avc3" - Need to check official name in spec.
    AVCConfigurationBox               0x61766343, // "avcC"
    MP4AudioSampleEntry               0x6d703461, // "mp4a"
    MP4VideoSampleEntry               0x6d703476, // "mp4v"
    ESDBox                            0x65736473, // "esds"
    VP8SampleEntry                    0x76703038, // "vp08"
    VP9SampleEntry                    0x76703039, // "vp09"
    VPCodecConfigurationBox           0x76706343, // "vpcC"
    AV1SampleEntry                    0x61763031, // "av01"
    AV1CodecConfigurationBox          0x61763143, // "av1C"
    FLACSampleEntry                   0x664c6143, // "fLaC"
    FLACSpecificBox                   0x64664c61, // "dfLa"
    OpusSampleEntry                   0x4f707573, // "Opus"
    OpusSpecificBox                   0x644f7073, // "dOps"
    ProtectedVisualSampleEntry        0x656e6376, // "encv" - Need to check official name in spec.
    ProtectedAudioSampleEntry         0x656e6361, // "enca" - Need to check official name in spec.
    MovieExtendsBox                   0x6d766578, // "mvex"
    MovieExtendsHeaderBox             0x6d656864, // "mehd"
    QTWaveAtom                        0x77617665, // "wave" - quicktime atom
    ProtectionSystemSpecificHeaderBox 0x70737368, // "pssh"
    SchemeInformationBox              0x73636869, // "schi"
    TrackEncryptionBox                0x74656e63, // "tenc"
    ProtectionSchemeInformationBox    0x73696e66, // "sinf"
    OriginalFormatBox                 0x66726d61, // "frma"
    SchemeTypeBox                     0x7363686d, // "schm"
    MP3AudioSampleEntry               0x2e6d7033, // ".mp3" - from F4V.
    CompositionOffsetBox              0x63747473, // "ctts"
    LPCMAudioSampleEntry              0x6C70636D, // "lpcm" - quicktime atom
    ALACSpecificBox                   0x616C6163, // "alac" - Also used by ALACSampleEntry
    UuidBox                           0x75756964, // "uuid"
    MetadataBox                       0x6d657461, // "meta" - IS this needed?
    MetadataHeaderBox                 0x6d686472, // "mhdr"
    MetadataItemKeysBox               0x6b657973, // "keys"
    MetadataItemListEntry             0x696c7374, // "ilst"
    MetadataItemDataEntry             0x64617461, // "data"
    MetadataItemNameBox               0x6e616d65, // "name"
    MetadataItemInformationBox        0x69746966, // "itif"
    UserdataBox                       0x75647461, // "udta"
    AlbumEntry                        0xa9616c62, // "©alb"
    ArtistEntry                       0xa9617274, // @art
    AlbumArtistEntry                  0x61415254, // aART
    CommentEntry                      0xa9636d74, // @cmt
    DateEntry                         0xa9646179, // @day
    TitleEntry                        0xa96e616d, // @nam
    CustomGenreEntry                  0xa967656e, // @gen
    StandardGenreEntry                0x676e7265, // gnre
    TrackNumberEntry                  0x74726b6e, // trkn
    DiskNumberEntry                   0x6469736b, // disk
    ComposerEntry                     0xa9777274, // @wrt
    EncoderEntry                      0xa9746f6f, // @too
    TempoEntry                        0x746d706f, // tmpo
    CopyrightEntry                    0x63707274, // cprt
    CompilationEntry                  0x6370696c, // cpil
    CoverArtEntry                     0x636f7672, // covr
    AdvisoryEntry                     0x72746e67, // rtng
    RatingEntry                       0x72617465, // rate
    GroupingEntry                     0xa9677270, // @grp
    MediaTypeEntry                    0x7374696b, //stik
    PodcastEntry                      0x70637374, //pcst
    CategoryEntry                     0x63617467, //catg
    KeywordEntry                      0x6b657977, //keyw
    PodcastUrlEntry                   0x7075726c, //purl
    PodcastGuidEntry                  0x65676964, //egid
    DescriptionEntry                  0x64657363, //desc
    LongDescriptionEntry              0x6c646573, //ldes
    LyricsEntry                       0xa96c7972, // @lyr
    TVNetworkNameEntry                0x74766e6e, //tvnn
    TVShowNameEntry                   0x74767368, //tvsh
    TVEpisodeNameEntry                0x7476656e, //tven
    TVSeasonEntry                     0x7476736e, //tvsn
    TVEpisodeNumberEntry              0x74766573, //tves
    PurchaseDateEntry                 0x70757264, //purd
    GaplessPlaybackEntry              0x70676170, //pgap
);

//
//@alb 0xa9616c62
//@art 0xa9617274
//aART 0x61415254
//@cmt 0xa9636d74
//@day 0xa9646179
//@nam 0xa96e616d
//@gen 0xa967656e
//gnre 0x676e7265
//trkn 0x74726b6e
//disk 0x6469736b
//@wrt 0xa9777274
//@too 0xa9746f6f
//tmpo 0x746d706f
//cprt 0x63707274
//cpil 0x6370696c
//covr 0x636f7672
//rtng 0x72746e67
//@grp 0xa9677270
//@lyr 0xa96c7972<|endoftext|>


<commit_before>use std::borrow::Cow::{Borrowed, Owned};

use pulldown_cmark as cmark;
use slug::slugify;
use syntect::easy::HighlightLines;
use syntect::html::{
    IncludeBackground, start_highlighted_html_snippet, styled_line_to_highlighted_html,
};

use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET};
use context::RenderContext;
use errors::Result;
use front_matter::InsertAnchor;
use link_checker::check_url;
use table_of_contents::{Header, make_table_of_contents, TempHeader};
use utils::site::resolve_internal_link;
use utils::vec::InsertMany;

use self::cmark::{Event, Options, Parser, Tag};

const CONTINUE_READING: &str = "

\n";
const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html";

#[derive(Debug)]
pub struct Rendered {
    pub body: String,
    pub summary_len: Option<usize>,
    pub toc: Vec,
}

struct HeaderIndex {
    start: usize,
    end: usize,
    level: i32,
}

impl HeaderIndex {
    fn new(start: usize, level: i32) -> HeaderIndex {
        HeaderIndex { start, end: 0, level }
    }
}

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name;
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

// Colocated asset links refers to the files in the same directory,
// there it should be a filename only
fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains('/')  // http://, ftp://, ../ etc
        && !link.starts_with("mailto:")
}

fn fix_link(link: &str, context: &RenderContext) -> Result<String> {
    // A few situations here:
    // - it could be a relative link (starting with `./`)
    // - it could be a link to a co-located asset
    // - it could be a normal link
    let result = if link.starts_with("./") {
        match resolve_internal_link(&link, context.permalinks) {
            Ok(url) => url,
            Err(_) => {
                return Err(format!("Relative link {} not found.", link).into());
            }
        }
    } else if is_colocated_asset_link(&link) {
        format!("{}{}", context.current_page_permalink, link)
    } else if context.config.check_external_links
        && !link.starts_with('#')
        && !link.starts_with("mailto:")
    {
        let res = check_url(&link);
        if res.is_valid() {
            link.to_string()
        } else {
            return Err(
                format!("Link {} is not valid: {}", link, res.message()).into(),
            );
        }
    } else {
        link.to_string()
    };
    Ok(result)
}

/// get only text in a slice of events
fn get_text(parser_slice: &[Event]) -> String {
    let mut title = String::new();

    for event in parser_slice.iter() {
        if let Event::Text(text) = event {
            title += text;
        }
    }

    title
}

fn get_header_indexes(events: &[Event]) -> Vec<HeaderIndex> {
    let mut header_indexes = vec![];

    for (i, event) in events.iter().enumerate() {
        match event {
            Event::Start(Tag::Header(level)) => {
                header_indexes.push(HeaderIndex::new(i, *level));
            }
            Event::End(Tag::Header(_)) => {
                let msg = "Header end before start?";
                header_indexes.last_mut().expect(msg).end = i;
            }
            _ => (),
        }
    }

    header_indexes
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut background = IncludeBackground::Yes;
    let mut highlighter: Option<(HighlightLines, bool)> = None;

    let mut inserted_anchors: Vec<String> = vec![];
    let mut headers: Vec<TempHeader> = vec![];

    let mut opts = Options::empty();
    let mut has_summary = false;
    opts.insert(Options::ENABLE_TABLES);
    opts.insert(Options::ENABLE_FOOTNOTES);

    {
        let mut events = Parser::new_ext(content, opts).map(|event| {
            match event {
                Event::Text(text) => {
                    // if we are in the middle of a code block
                    if let Some((ref mut highlighter, in_extra)) = highlighter {
                        let highlighted = if in_extra {
                            if let Some(ref extra) = context.config.extra_syntax_set {
                                highlighter.highlight(&text, &extra)
                            } else {
                                unreachable!("Got a highlighter from extra syntaxes but no extra?");
                            }
                        } else {
                            highlighter.highlight(&text, &SYNTAX_SET)
                        };
                        //let highlighted = &highlighter.highlight(&text, ss);
                        let html = styled_line_to_highlighted_html(&highlighted, background);
                        return Event::Html(Owned(html));
                    }

                    // Business as usual
                    Event::Text(text)
                }
                Event::Start(Tag::CodeBlock(ref info)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
"));
                    }

                    let theme = &THEME_SET.themes[&context.config.highlight_theme];
                    highlighter = Some(get_highlighter(info, &context.config));
                    // This selects the background color the same way that start_coloured_html_snippet does
                    let color =
                        theme.settings.background.unwrap_or(::syntect::highlighting::Color::WHITE);
                    background = IncludeBackground::IfDifferent(color);
                    let snippet = start_highlighted_html_snippet(theme);
                    Event::Html(Owned(snippet.0))
                }
                Event::End(Tag::CodeBlock(_)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
\n"));
                    }
                    // reset highlight and close the code block
                    highlighter = None;
                    Event::Html(Borrowed("
"))
                }
                Event::Start(Tag::Image(src, title)) => {
                    if is_colocated_asset_link(&src) {
                        return Event::Start(Tag::Image(
                            Owned(format!("{}{}", context.current_page_permalink, src)),
                            title,
                        ));
                    }

                    Event::Start(Tag::Image(src, title))
                }
                Event::Start(Tag::Link(link, title)) => {
                    let fixed_link = match fix_link(&link, context) {
                        Ok(fixed_link) => fixed_link,
                        Err(err) => {
                            error = Some(err);
                            return Event::Html(Borrowed(""))
                        }
                    };

                    Event::Start(Tag::Link(Owned(fixed_link), title))
                }
                Event::Html(ref markup) if markup.contains("") => {
                    has_summary = true;
                    Event::Html(Borrowed(CONTINUE_READING))
                }
                _ => event,
            }
        }).collect::<Vec<_>>(); // We need to collect the events to make a second pass

        let mut header_indexes = get_header_indexes(&events);

        let mut anchors_to_insert = vec![];

        for header_idx in header_indexes {
            let start_idx = header_idx.start;
            let end_idx = header_idx.end;
            let title = get_text(&events[start_idx + 1 .. end_idx]);
            let id = find_anchor(&inserted_anchors, slugify(&title), 0);
            inserted_anchors.push(id.clone());

            // insert `id` to the tag
            let html = format!("<h{lvl} id=\"{id}\">", lvl = header_idx.level, id = id);
            events[start_idx] = Event::Html(Owned(html));

            // generate anchors and places to insert them
            if context.insert_anchor != InsertAnchor::None {
                let anchor_idx = match context.insert_anchor {
                    InsertAnchor::Left => start_idx + 1,
                    InsertAnchor::Right => end_idx,
                    InsertAnchor::None => 0, // Not important
                };
                let mut c = tera::Context::new();
                c.insert("id", &id);
                let anchor_link = context.tera.render(ANCHOR_LINK_TEMPLATE, &c).unwrap();
                anchors_to_insert.push((anchor_idx, Event::Html(Owned(anchor_link))));
            }

            // record header to make table of contents
            let permalink = format!("{}#{}", context.current_page_permalink, id);
            let temp_header = TempHeader { level: header_idx.level, id, permalink, title };
            headers.push(temp_header);
        }

        if context.insert_anchor != InsertAnchor::None {
            events.insert_many(anchors_to_insert);
        }

        cmark::html::push_html(&mut html, events.into_iter());
    }

    if let Some(e) = error {
        return Err(e);
    } else {
        Ok(Rendered {
            summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
            body: html,
            toc: make_table_of_contents(&headers),
        })
    }
}
<commit_msg>derive Debug for HeaderIndex<commit_after>use std::borrow::Cow::{Borrowed, Owned};

use pulldown_cmark as cmark;
use slug::slugify;
use syntect::easy::HighlightLines;
use syntect::html::{
    IncludeBackground, start_highlighted_html_snippet, styled_line_to_highlighted_html,
};

use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET};
use context::RenderContext;
use errors::Result;
use front_matter::InsertAnchor;
use link_checker::check_url;
use table_of_contents::{Header, make_table_of_contents, TempHeader};
use utils::site::resolve_internal_link;
use utils::vec::InsertMany;

use self::cmark::{Event, Options, Parser, Tag};

const CONTINUE_READING: &str = "

\n";
const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html";

#[derive(Debug)]
pub struct Rendered {
    pub body: String,
    pub summary_len: Option<usize>,
    pub toc: Vec,
}

#[derive(Debug)]
struct HeaderIndex {
    start: usize,
    end: usize,
    level: i32,
}

impl HeaderIndex {
    fn new(start: usize, level: i32) -> HeaderIndex {
        HeaderIndex { start, end: 0, level }
    }
}

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name;
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

// Colocated asset links refers to the files in the same directory,
// there it should be a filename only
fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains('/')  // http://, ftp://, ../ etc
        && !link.starts_with("mailto:")
}

fn fix_link(link: &str, context: &RenderContext) -> Result<String> {
    // A few situations here:
    // - it could be a relative link (starting with `./`)
    // - it could be a link to a co-located asset
    // - it could be a normal link
    let result = if link.starts_with("./") {
        match resolve_internal_link(&link, context.permalinks) {
            Ok(url) => url,
            Err(_) => {
                return Err(format!("Relative link {} not found.", link).into());
            }
        }
    } else if is_colocated_asset_link(&link) {
        format!("{}{}", context.current_page_permalink, link)
    } else if context.config.check_external_links
        && !link.starts_with('#')
        && !link.starts_with("mailto:")
    {
        let res = check_url(&link);
        if res.is_valid() {
            link.to_string()
        } else {
            return Err(
                format!("Link {} is not valid: {}", link, res.message()).into(),
            );
        }
    } else {
        link.to_string()
    };
    Ok(result)
}

/// get only text in a slice of events
fn get_text(parser_slice: &[Event]) -> String {
    let mut title = String::new();

    for event in parser_slice.iter() {
        if let Event::Text(text) = event {
            title += text;
        }
    }

    title
}

fn get_header_indexes(events: &[Event]) -> Vec<HeaderIndex> {
    let mut header_indexes = vec![];

    for (i, event) in events.iter().enumerate() {
        match event {
            Event::Start(Tag::Header(level)) => {
                header_indexes.push(HeaderIndex::new(i, *level));
            }
            Event::End(Tag::Header(_)) => {
                let msg = "Header end before start?";
                header_indexes.last_mut().expect(msg).end = i;
            }
            _ => (),
        }
    }

    header_indexes
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut background = IncludeBackground::Yes;
    let mut highlighter: Option<(HighlightLines, bool)> = None;

    let mut inserted_anchors: Vec<String> = vec![];
    let mut headers: Vec<TempHeader> = vec![];

    let mut opts = Options::empty();
    let mut has_summary = false;
    opts.insert(Options::ENABLE_TABLES);
    opts.insert(Options::ENABLE_FOOTNOTES);

    {
        let mut events = Parser::new_ext(content, opts).map(|event| {
            match event {
                Event::Text(text) => {
                    // if we are in the middle of a code block
                    if let Some((ref mut highlighter, in_extra)) = highlighter {
                        let highlighted = if in_extra {
                            if let Some(ref extra) = context.config.extra_syntax_set {
                                highlighter.highlight(&text, &extra)
                            } else {
                                unreachable!("Got a highlighter from extra syntaxes but no extra?");
                            }
                        } else {
                            highlighter.highlight(&text, &SYNTAX_SET)
                        };
                        //let highlighted = &highlighter.highlight(&text, ss);
                        let html = styled_line_to_highlighted_html(&highlighted, background);
                        return Event::Html(Owned(html));
                    }

                    // Business as usual
                    Event::Text(text)
                }
                Event::Start(Tag::CodeBlock(ref info)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
"));
                    }

                    let theme = &THEME_SET.themes[&context.config.highlight_theme];
                    highlighter = Some(get_highlighter(info, &context.config));
                    // This selects the background color the same way that start_coloured_html_snippet does
                    let color =
                        theme.settings.background.unwrap_or(::syntect::highlighting::Color::WHITE);
                    background = IncludeBackground::IfDifferent(color);
                    let snippet = start_highlighted_html_snippet(theme);
                    Event::Html(Owned(snippet.0))
                }
                Event::End(Tag::CodeBlock(_)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Borrowed("
\n"));
                    }
                    // reset highlight and close the code block
                    highlighter = None;
                    Event::Html(Borrowed("
"))
                }
                Event::Start(Tag::Image(src, title)) => {
                    if is_colocated_asset_link(&src) {
                        return Event::Start(Tag::Image(
                            Owned(format!("{}{}", context.current_page_permalink, src)),
                            title,
                        ));
                    }

                    Event::Start(Tag::Image(src, title))
                }
                Event::Start(Tag::Link(link, title)) => {
                    let fixed_link = match fix_link(&link, context) {
                        Ok(fixed_link) => fixed_link,
                        Err(err) => {
                            error = Some(err);
                            return Event::Html(Borrowed(""))
                        }
                    };

                    Event::Start(Tag::Link(Owned(fixed_link), title))
                }
                Event::Html(ref markup) if markup.contains("") => {
                    has_summary = true;
                    Event::Html(Borrowed(CONTINUE_READING))
                }
                _ => event,
            }
        }).collect::<Vec<_>>(); // We need to collect the events to make a second pass

        let mut header_indexes = get_header_indexes(&events);

        let mut anchors_to_insert = vec![];

        for header_idx in header_indexes {
            let start_idx = header_idx.start;
            let end_idx = header_idx.end;
            let title = get_text(&events[start_idx + 1 .. end_idx]);
            let id = find_anchor(&inserted_anchors, slugify(&title), 0);
            inserted_anchors.push(id.clone());

            // insert `id` to the tag
            let html = format!("<h{lvl} id=\"{id}\">", lvl = header_idx.level, id = id);
            events[start_idx] = Event::Html(Owned(html));

            // generate anchors and places to insert them
            if context.insert_anchor != InsertAnchor::None {
                let anchor_idx = match context.insert_anchor {
                    InsertAnchor::Left => start_idx + 1,
                    InsertAnchor::Right => end_idx,
                    InsertAnchor::None => 0, // Not important
                };
                let mut c = tera::Context::new();
                c.insert("id", &id);
                let anchor_link = context.tera.render(ANCHOR_LINK_TEMPLATE, &c).unwrap();
                anchors_to_insert.push((anchor_idx, Event::Html(Owned(anchor_link))));
            }

            // record header to make table of contents
            let permalink = format!("{}#{}", context.current_page_permalink, id);
            let temp_header = TempHeader { level: header_idx.level, id, permalink, title };
            headers.push(temp_header);
        }

        if context.insert_anchor != InsertAnchor::None {
            events.insert_many(anchors_to_insert);
        }

        cmark::html::push_html(&mut html, events.into_iter());
    }

    if let Some(e) = error {
        return Err(e);
    } else {
        Ok(Rendered {
            summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
            body: html,
            toc: make_table_of_contents(&headers),
        })
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The tiny-http Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Result as IoResult;
use std::io::{Read, Write};

use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};

pub struct SequentialReaderBuilder<R> where R: Read + Send {
    reader: Arc<Mutex<R>>,
    next_trigger: Option<Receiver<()>>,
}

pub struct SequentialReader<R> where R: Read + Send {
    trigger: Option<Receiver<()>>,
    reader: Arc<Mutex<R>>,
    on_finish: Sender<()>,
}

pub struct SequentialWriterBuilder<W> where W: Write + Send {
    writer: Arc<Mutex<W>>,
    next_trigger: Option<Receiver<()>>,
}

pub struct SequentialWriter<W> where W: Write + Send {
    trigger: Option<Receiver<()>>,
    writer: Arc<Mutex<W>>,
    on_finish: Sender<()>,
}

impl<R: Read + Send> SequentialReaderBuilder<R> {
    pub fn new(reader: R) -> SequentialReaderBuilder<R> {
        SequentialReaderBuilder {
            reader: Arc::new(Mutex::new(reader)),
            next_trigger: None,
        }
    }
}

impl<W: Write + Send> SequentialWriterBuilder<W> {
    pub fn new(writer: W) -> SequentialWriterBuilder<W> {
        SequentialWriterBuilder {
            writer: Arc::new(Mutex::new(writer)),
            next_trigger: None,
        }
    }
}

impl<R: Read + Send> Iterator for SequentialReaderBuilder<R> {
    type Item = SequentialReader<R>;
    fn next(&mut self) -> Option<SequentialReader<R>> {
        let (tx, rx) = channel();
        let mut next_next_trigger = Some(rx);
        ::std::mem::swap(&mut next_next_trigger, &mut self.next_trigger);

        Some(SequentialReader {
            trigger: next_next_trigger,
            reader: self.reader.clone(),
            on_finish: tx,
        })
    }
}

impl<W: Write + Send> Iterator for SequentialWriterBuilder<W> {
    type Item = SequentialWriter<W>;
    fn next(&mut self) -> Option<SequentialWriter<W>> {
        let (tx, rx) = channel();
        let mut next_next_trigger = Some(rx);
        ::std::mem::swap(&mut next_next_trigger, &mut self.next_trigger);

        Some(SequentialWriter {
            trigger: next_next_trigger,
            writer: self.writer.clone(),
            on_finish: tx,
        })
    }
}

impl<R: Read + Send> Read for SequentialReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        self.trigger.as_mut().map(|v| v.recv().unwrap());
        self.trigger = None;

        self.reader.lock().unwrap().read(buf)
    }
}

impl<W: Write + Send> Write for SequentialWriter<W> {
    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
        self.trigger.as_mut().map(|v| v.recv().unwrap());
        self.trigger = None;

        self.writer.lock().unwrap().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        self.trigger.as_mut().map(|v| v.recv().unwrap());
        self.trigger = None;

        self.writer.lock().unwrap().flush()
    }
}

impl<R> Drop for SequentialReader<R> where R: Read + Send {
    fn drop(&mut self) {
        self.on_finish.send(()).ok();
    }
}

impl<W> Drop for SequentialWriter<W> where W: Write + Send {
    fn drop(&mut self) {
        self.on_finish.send(()).ok();
    }
}
<commit_msg>Cleanup the sequential reader<commit_after>// Copyright 2015 The tiny-http Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Result as IoResult;
use std::io::{Read, Write};

use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};

use std::mem;

pub struct SequentialReaderBuilder<R> where R: Read + Send {
    inner: SequentialReaderBuilderInner<R>,
}

enum SequentialReaderBuilderInner<R> where R: Read + Send {
    First(R),
    NotFirst(Receiver<R>),
}

pub struct SequentialReader<R> where R: Read + Send {
    inner: SequentialReaderInner<R>,
    next: Sender<R>,
}

enum SequentialReaderInner<R> where R: Read + Send {
    MyTurn(R),
    Waiting(Receiver<R>),
    Empty,
}

pub struct SequentialWriterBuilder<W> where W: Write + Send {
    writer: Arc<Mutex<W>>,
    next_trigger: Option<Receiver<()>>,
}

pub struct SequentialWriter<W> where W: Write + Send {
    trigger: Option<Receiver<()>>,
    writer: Arc<Mutex<W>>,
    on_finish: Sender<()>,
}

impl<R: Read + Send> SequentialReaderBuilder<R> {
    pub fn new(reader: R) -> SequentialReaderBuilder<R> {
        SequentialReaderBuilder {
            inner: SequentialReaderBuilderInner::First(reader),
        }
    }
}

impl<W: Write + Send> SequentialWriterBuilder<W> {
    pub fn new(writer: W) -> SequentialWriterBuilder<W> {
        SequentialWriterBuilder {
            writer: Arc::new(Mutex::new(writer)),
            next_trigger: None,
        }
    }
}

impl<R: Read + Send> Iterator for SequentialReaderBuilder<R> {
    type Item = SequentialReader<R>;

    fn next(&mut self) -> Option<SequentialReader<R>> {
        let (tx, rx) = channel();

        let inner = mem::replace(&mut self.inner, SequentialReaderBuilderInner::NotFirst(rx));

        match inner {
            SequentialReaderBuilderInner::First(reader) => {
                Some(SequentialReader {
                    inner: SequentialReaderInner::MyTurn(reader),
                    next: tx,
                })
            },

            SequentialReaderBuilderInner::NotFirst(previous) => {
                Some(SequentialReader {
                    inner: SequentialReaderInner::Waiting(previous),
                    next: tx,
                })
            },
        }
    }
}

impl<W: Write + Send> Iterator for SequentialWriterBuilder<W> {
    type Item = SequentialWriter<W>;
    fn next(&mut self) -> Option<SequentialWriter<W>> {
        let (tx, rx) = channel();
        let mut next_next_trigger = Some(rx);
        ::std::mem::swap(&mut next_next_trigger, &mut self.next_trigger);

        Some(SequentialWriter {
            trigger: next_next_trigger,
            writer: self.writer.clone(),
            on_finish: tx,
        })
    }
}

impl<R: Read + Send> Read for SequentialReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        let mut reader = match self.inner {
            SequentialReaderInner::MyTurn(ref mut reader) => return reader.read(buf),
            SequentialReaderInner::Waiting(ref mut recv) => recv.recv().unwrap(),
            SequentialReaderInner::Empty => unreachable!(),
        };

        let result = reader.read(buf);
        self.inner = SequentialReaderInner::MyTurn(reader);
        result
    }
}

impl<W: Write + Send> Write for SequentialWriter<W> {
    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
        self.trigger.as_mut().map(|v| v.recv().unwrap());
        self.trigger = None;

        self.writer.lock().unwrap().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        self.trigger.as_mut().map(|v| v.recv().unwrap());
        self.trigger = None;

        self.writer.lock().unwrap().flush()
    }
}

impl<R> Drop for SequentialReader<R> where R: Read + Send {
    fn drop(&mut self) {
        let inner = mem::replace(&mut self.inner, SequentialReaderInner::Empty);

        match inner {
            SequentialReaderInner::MyTurn(reader) => {
                self.next.send(reader).ok();
            },
            SequentialReaderInner::Waiting(recv) => {
                let reader = recv.recv().unwrap();
                self.next.send(reader).ok();
            },
            SequentialReaderInner::Empty => (),
        }
    }
}

impl<W> Drop for SequentialWriter<W> where W: Write + Send {
    fn drop(&mut self) {
        self.on_finish.send(()).ok();
    }
}
<|endoftext|>


<commit_before>use crate::v3_0::{
    Callback, Example, Header, Link, Parameter, RequestBody, Response, Schema, SecurityScheme,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum ObjectOrReference<T> {
    Object(T),
    Ref {
        #[serde(rename = "$ref")]
        ref_path: String,
    },
}

/// Holds a set of reusable objects for different aspects of the OAS.
///
/// All objects defined within the components object will have no effect on the API unless
/// they are explicitly referenced from properties outside the components object.
///
/// See <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#componentsObject>.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Components {
    /// An object to hold reusable Schema Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schemas: Option<BTreeMap<String, ObjectOrReference<Schema>>>,

    /// An object to hold reusable Response Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub responses: Option<BTreeMap<String, ObjectOrReference<Response>>>,

    /// An object to hold reusable Parameter Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<BTreeMap<String, ObjectOrReference<Parameter>>>,

    /// An object to hold reusable Example
    #[serde(skip_serializing_if = "Option::is_none")]
    pub examples: Option<BTreeMap<String, ObjectOrReference<Example>>>,

    /// An object to hold reusable Request Body Objects.
    #[serde(skip_serializing_if = "Option::is_none", rename = "requestBodies")]
    pub request_bodies: Option<BTreeMap<String, ObjectOrReference<RequestBody>>>,

    /// An object to hold reusable Header Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headers: Option<BTreeMap<String, ObjectOrReference<Header>>>,

    /// An object to hold reusable Security Scheme Objects.
    #[serde(skip_serializing_if = "Option::is_none", rename = "securitySchemes")]
    pub security_schemes: Option<BTreeMap<String, ObjectOrReference<SecurityScheme>>>,

    /// An object to hold reusable Link Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<BTreeMap<String, ObjectOrReference<Link>>>,

    /// An object to hold reusable Callback Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub callbacks: Option<BTreeMap<String, ObjectOrReference<Callback>>>,
    // TODO: Add "Specification Extensions" https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#specificationExtensions}
}
<commit_msg>feat(extensions): add extension to components<commit_after>use crate::v3_0::{
    Callback, Example, Extensions, Header, Link, Parameter, RequestBody, Response, Schema,
    SecurityScheme,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum ObjectOrReference<T> {
    Object(T),
    Ref {
        #[serde(rename = "$ref")]
        ref_path: String,
    },
}

/// Holds a set of reusable objects for different aspects of the OAS.
///
/// All objects defined within the components object will have no effect on the API unless
/// they are explicitly referenced from properties outside the components object.
///
/// See <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#componentsObject>.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Components {
    /// An object to hold reusable Schema Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schemas: Option<BTreeMap<String, ObjectOrReference<Schema>>>,

    /// An object to hold reusable Response Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub responses: Option<BTreeMap<String, ObjectOrReference<Response>>>,

    /// An object to hold reusable Parameter Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<BTreeMap<String, ObjectOrReference<Parameter>>>,

    /// An object to hold reusable Example
    #[serde(skip_serializing_if = "Option::is_none")]
    pub examples: Option<BTreeMap<String, ObjectOrReference<Example>>>,

    /// An object to hold reusable Request Body Objects.
    #[serde(skip_serializing_if = "Option::is_none", rename = "requestBodies")]
    pub request_bodies: Option<BTreeMap<String, ObjectOrReference<RequestBody>>>,

    /// An object to hold reusable Header Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headers: Option<BTreeMap<String, ObjectOrReference<Header>>>,

    /// An object to hold reusable Security Scheme Objects.
    #[serde(skip_serializing_if = "Option::is_none", rename = "securitySchemes")]
    pub security_schemes: Option<BTreeMap<String, ObjectOrReference<SecurityScheme>>>,

    /// An object to hold reusable Link Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<BTreeMap<String, ObjectOrReference<Link>>>,

    /// An object to hold reusable Callback Objects.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub callbacks: Option<BTreeMap<String, ObjectOrReference<Callback>>>,

    #[serde(flatten)]
    pub extensions: Extensions,
}
<|endoftext|>


<commit_before>use std::io::{BufReader, BufRead, Read};
use std::fs::File;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;

use rustc_serialize::json;

use config::Config;
use config::read_config;
use config::builders::{Builder};
use config::builders::Builder as B;
use config::builders::Source as S;
use shaman::sha2::Sha256;
use shaman::digest::Digest;
use container::vagga::container_ver;
use self::HashResult::*;

pub enum HashResult {
    Hashed,
    New,
    Error(String)
}

pub trait VersionHash {
    fn hash(&self, cfg: &Config, hash: &mut Digest) -> HashResult;
}

impl VersionHash for Builder {
    fn hash(&self, cfg: &Config, hash: &mut Digest) -> HashResult {
        match self {
            &B::Py2Requirements(ref fname) | &B::Py3Requirements(ref fname)
            => {
                match
                    File::open(&Path::new("/work").join(fname))
                    .and_then(|f| {
                        let mut f = BufReader::new(f);
                        for line in f.lines() {
                            let line = try!(line);
                            let chunk = line[..].trim();
                            // Ignore empty lines and comments
                            if chunk.len() == 0 || chunk.starts_with("#") {
                                continue;
                            }
                            // Should we also ignore the order?
                            hash.input(chunk.as_bytes());
                        }
                        Ok(())
                    })
                {
                    Err(e) => return Error(format!("Can't read file: {}", e)),
                    Ok(()) => return Hashed,
                }
            }
            &B::Depends(ref filename) => {
                match
                    File::open(&Path::new("/work").join(filename))
                    .and_then(|mut f| {
                        loop {
                            let mut chunk = [0u8; 128*1024];
                            let bytes = match f.read(&mut chunk[..]) {
                                Ok(0) => break,
                                Ok(bytes) => bytes,
                                Err(e) => return Err(e),
                            };
                            hash.input(&chunk[..bytes]);
                        }
                        Ok(())
                    })
                {
                    Err(e) => return Error(format!("Can't read file: {}", e)),
                    Ok(()) => return Hashed,
                }
            }
            &B::Container(ref name) => {
                let cont = match cfg.containers.get(name) {
                    Some(cont) => cont,
                    None => {
                        return Error(format!("Container {:?} not found",
                                             name));
                    }
                };
                for b in cont.setup.iter() {
                    debug!("Versioning setup: {:?}", b);
                    match b.hash(cfg, hash) {
                        Hashed => continue,
                        New => return New,  // Always rebuild
                        Error(e) => {
                            return Error(format!("{:?}: {}", name, e));
                        }
                    }
                }
                Hashed
            }
            &B::SubConfig(ref sconfig) => {
                let path = match sconfig.source {
                    S::Container(ref container) => {
                        let version = match container_ver(container) {
                            Ok(ver) => ver,
                            Err(_) => return New,  // TODO(tailhook) better check
                        };
                        Path::new("/vagga/base/.roots")
                            .join(version).join("root")
                            .join(&sconfig.path)
                    }
                    S::Git(ref git) => {
                        unimplemented!();
                    }
                    S::Directory => {
                        Path::new("/work").join(&sconfig.path)
                    }
                };
                let subcfg = match read_config(&path) {
                    Ok(cfg) => cfg,
                    Err(e) => return Error(e),
                };
                let cont = match subcfg.containers.get(&sconfig.container) {
                    Some(cont) => cont,
                    None => {
                        return Error(format!(
                            "Container {:?} not found in {:?}",
                            sconfig.container, sconfig.path));
                    }
                };
                for b in cont.setup.iter() {
                    debug!("Versioning setup: {:?}", b);
                    match b.hash(cfg, hash) {
                        Hashed => continue,
                        New => return New,  // Always rebuild
                        Error(e) => {
                            return Error(format!("{:?}: {}",
                                sconfig.container, e));
                        }
                    }
                }
                Hashed
            }
            &B::CacheDirs(ref map) => {
                for (k, v) in map.iter() {
                    hash.input(k.as_os_str().as_bytes());
                    hash.input(b"\0");
                    hash.input(v.as_bytes());
                    hash.input(b"\0");
                }
                Hashed
            }
            &B::Text(ref map) => {
                for (k, v) in map.iter() {
                    hash.input(k.as_os_str().as_bytes());
                    hash.input(b"\0");
                    hash.input(v.as_bytes());
                    hash.input(b"\0");
                }
                Hashed
            }
            _ => {
                hash.input(json::encode(self).unwrap().as_bytes());
                Hashed
            }
        }
    }
}
<commit_msg>Smaller buffer for reading file (because stack issue when using musl)<commit_after>use std::io::{BufReader, BufRead, Read};
use std::fs::File;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;

use rustc_serialize::json;

use config::Config;
use config::read_config;
use config::builders::{Builder};
use config::builders::Builder as B;
use config::builders::Source as S;
use shaman::sha2::Sha256;
use shaman::digest::Digest;
use container::vagga::container_ver;
use self::HashResult::*;

pub enum HashResult {
    Hashed,
    New,
    Error(String)
}

pub trait VersionHash {
    fn hash(&self, cfg: &Config, hash: &mut Digest) -> HashResult;
}

impl VersionHash for Builder {
    fn hash(&self, cfg: &Config, hash: &mut Digest) -> HashResult {
        match self {
            &B::Py2Requirements(ref fname) | &B::Py3Requirements(ref fname)
            => {
                match
                    File::open(&Path::new("/work").join(fname))
                    .and_then(|f| {
                        let mut f = BufReader::new(f);
                        for line in f.lines() {
                            let line = try!(line);
                            let chunk = line[..].trim();
                            // Ignore empty lines and comments
                            if chunk.len() == 0 || chunk.starts_with("#") {
                                continue;
                            }
                            // Should we also ignore the order?
                            hash.input(chunk.as_bytes());
                        }
                        Ok(())
                    })
                {
                    Err(e) => return Error(format!("Can't read file: {}", e)),
                    Ok(()) => return Hashed,
                }
            }
            &B::Depends(ref filename) => {
                match
                    File::open(&Path::new("/work").join(filename))
                    .and_then(|mut f| {
                        loop {
                            let mut chunk = [0u8; 8*1024];
                            let bytes = match f.read(&mut chunk[..]) {
                                Ok(0) => break,
                                Ok(bytes) => bytes,
                                Err(e) => return Err(e),
                            };
                            hash.input(&chunk[..bytes]);
                        }
                        Ok(())
                    })
                {
                    Err(e) => return Error(format!("Can't read file: {}", e)),
                    Ok(()) => return Hashed,
                }
            }
            &B::Container(ref name) => {
                let cont = match cfg.containers.get(name) {
                    Some(cont) => cont,
                    None => {
                        return Error(format!("Container {:?} not found",
                                             name));
                    }
                };
                for b in cont.setup.iter() {
                    debug!("Versioning setup: {:?}", b);
                    match b.hash(cfg, hash) {
                        Hashed => continue,
                        New => return New,  // Always rebuild
                        Error(e) => {
                            return Error(format!("{:?}: {}", name, e));
                        }
                    }
                }
                Hashed
            }
            &B::SubConfig(ref sconfig) => {
                let path = match sconfig.source {
                    S::Container(ref container) => {
                        let version = match container_ver(container) {
                            Ok(ver) => ver,
                            Err(_) => return New,  // TODO(tailhook) better check
                        };
                        Path::new("/vagga/base/.roots")
                            .join(version).join("root")
                            .join(&sconfig.path)
                    }
                    S::Git(ref git) => {
                        unimplemented!();
                    }
                    S::Directory => {
                        Path::new("/work").join(&sconfig.path)
                    }
                };
                let subcfg = match read_config(&path) {
                    Ok(cfg) => cfg,
                    Err(e) => return Error(e),
                };
                let cont = match subcfg.containers.get(&sconfig.container) {
                    Some(cont) => cont,
                    None => {
                        return Error(format!(
                            "Container {:?} not found in {:?}",
                            sconfig.container, sconfig.path));
                    }
                };
                for b in cont.setup.iter() {
                    debug!("Versioning setup: {:?}", b);
                    match b.hash(cfg, hash) {
                        Hashed => continue,
                        New => return New,  // Always rebuild
                        Error(e) => {
                            return Error(format!("{:?}: {}",
                                sconfig.container, e));
                        }
                    }
                }
                Hashed
            }
            &B::CacheDirs(ref map) => {
                for (k, v) in map.iter() {
                    hash.input(k.as_os_str().as_bytes());
                    hash.input(b"\0");
                    hash.input(v.as_bytes());
                    hash.input(b"\0");
                }
                Hashed
            }
            &B::Text(ref map) => {
                for (k, v) in map.iter() {
                    hash.input(k.as_os_str().as_bytes());
                    hash.input(b"\0");
                    hash.input(v.as_bytes());
                    hash.input(b"\0");
                }
                Hashed
            }
            _ => {
                hash.input(json::encode(self).unwrap().as_bytes());
                Hashed
            }
        }
    }
}
<|endoftext|>


<commit_before>//! An enum mapping type.
//!
//! It is implemented using array, so using it is as fast as using Rust
//! arrays.

#![no_std]
#![deny(missing_docs)]

use core::hash::{Hash, Hasher};
use core::iter::Enumerate;
use core::marker::PhantomData;
use core::ops::{Index, IndexMut};
use core::slice;

// this is not stable, do not depend on this
#[doc(hidden)]
pub trait Internal<V>: Sized {
    type Array;
    fn slice(&Self::Array) -> &[V];
    fn slice_mut(&mut Self::Array) -> &mut [V];
    fn from_usize(usize) -> Self;
    fn to_usize(self) -> usize;
    fn from_function<F: Fn(Self) -> V>(F) -> Self::Array;
}

/// An enum mapping.
///
/// This internally uses an array which stores a value for each possible
/// enum value. To work, it requires implementation of internal (private,
/// although public due to macro limitations) trait which allows extracting
/// information about an enum, which can be automatically generated using
/// `#[derive(EnumMap)]` from `enum_map_derive` crate.
///
/// # Examples
///
/// ```
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// use enum_map::EnumMap;
///
/// #[derive(EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
/// }
///
/// fn main() {
///     let mut map = EnumMap::new();
///     // new initializes map with default values
///     assert_eq!(map[Example::A], 0);
///     map[Example::A] = 3;
///     assert_eq!(map[Example::A], 3);
/// }
/// ```
#[derive(Debug)]
pub struct EnumMap<K: Internal<V>, V> {
    array: K::Array,
}

impl<K: Internal<V>, V> EnumMap<K, V> {
    /// Returns an iterator over enum map.
    pub fn iter(&self) -> Iter<K, V> {
        self.into_iter()
    }
}

impl<F: Fn(K) -> V, K: Internal<V>, V> From<F> for EnumMap<K, V> {
    fn from(f: F) -> Self {
        EnumMap { array: K::from_function(f) }
    }
}

/// Enum map constructor.
///
/// This macro allows to create a new enum map in a type safe way. It takes
/// a list of `,` separated pairs separated by `=>`. Left side is `|`
/// separated list of enum keys, or `_` to match all unmatched enum keys,
/// while right side is a value.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// #[derive(EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
///     D,
/// }
///
/// fn main() {
///     let enum_map = enum_map! {
///         Example::A | Example::B => 1,
///         Example::C => 2,
///         _ => 3,
///     };
///     assert_eq!(enum_map[Example::A], 1);
///     assert_eq!(enum_map[Example::B], 1);
///     assert_eq!(enum_map[Example::C], 2);
///     assert_eq!(enum_map[Example::D], 3);
/// }
/// ```
#[macro_export]
macro_rules! enum_map {
    {$($t:tt)*} => {
        ::enum_map::EnumMap::from(|k| match k { $($t)* })
    };
}

impl<K: Internal<V>, V: Default> EnumMap<K, V>
    where K::Array: Default
{
    /// Creates an enum map with default values.
    ///
    /// ```
    /// extern crate enum_map;
    /// #[macro_use]
    /// extern crate enum_map_derive;
    ///
    /// use enum_map::EnumMap;
    ///
    /// #[derive(EnumMap)]
    /// enum Example {
    ///     A,
    /// }
    ///
    /// fn main() {
    ///     let enum_map = EnumMap::<_, i32>::new();
    ///     assert_eq!(enum_map[Example::A], 0);
    /// }
    /// ```
    pub fn new() -> Self {
        EnumMap::default()
    }
}

// Implementations provided by derive attribute are too specific, and put requirements on K.
// This is caused by rust-lang/rust#26925.
impl<K: Internal<V>, V> Clone for EnumMap<K, V>
    where K::Array: Clone
{
    fn clone(&self) -> Self {
        EnumMap { array: self.array.clone() }
    }
}

impl<K: Internal<V>, V> Copy for EnumMap<K, V> where K::Array: Copy {}

impl<K: Internal<V>, V> PartialEq for EnumMap<K, V>
    where K::Array: PartialEq
{
    fn eq(&self, other: &Self) -> bool {
        self.array == other.array
    }
}

impl<K: Internal<V>, V> Eq for EnumMap<K, V> where K::Array: Eq {}

impl<K: Internal<V>, V> Hash for EnumMap<K, V>
    where K::Array: Hash
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.array.hash(state);
    }
}

impl<K: Internal<V>, V> Default for EnumMap<K, V>
    where K::Array: Default
{
    fn default() -> Self {
        EnumMap { array: K::Array::default() }
    }
}

impl<K: Internal<V>, V> Index<K> for EnumMap<K, V> {
    type Output = V;

    fn index(&self, key: K) -> &V {
        &K::slice(&self.array)[key.to_usize()]
    }
}

impl<K: Internal<V>, V> IndexMut<K> for EnumMap<K, V> {
    fn index_mut(&mut self, key: K) -> &mut V {
        &mut K::slice_mut(&mut self.array)[key.to_usize()]
    }
}

/// Immutable enum map iterator
///
/// This struct is created by `iter` method or `into_iter` on a reference
/// to `EnumMap`.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// #[derive(EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
/// }
///
/// fn main() {
///     let mut map = enum_map! { Example::A => 3, _ => 0 };
///     assert_eq!(map[Example::A], 3);
///     for (key, &value) in &map {
///         assert_eq!(match key {
///             Example::A => 3,
///             _ => 0,
///         }, value);
///     }
/// }
/// ```
pub struct Iter<'a, K, V: 'a> {
    _phantom: PhantomData<K>,
    iterator: Enumerate<slice::Iter<'a, V>>,
}

impl<'a, K: Internal<V>, V> Iterator for Iter<'a, K, V> {
    type Item = (K, &'a V);
    fn next(&mut self) -> Option<Self::Item> {
        self.iterator
            .next()
            .map(|(index, item)| (K::from_usize(index), item))
    }
}

impl<'a, K: Internal<V>, V> IntoIterator for &'a EnumMap<K, V> {
    type Item = (K, &'a V);
    type IntoIter = Iter<'a, K, V>;
    fn into_iter(self) -> Self::IntoIter {
        Iter {
            _phantom: PhantomData,
            iterator: K::slice(&self.array).iter().enumerate(),
        }
    }
}

/// Mutable map iterator
///
/// This struct is created by `iter_mut` method or `into_iter` on a mutable
/// reference to `EnumMap`.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// #[derive(Debug, EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
/// }
///
/// fn main() {
///     let mut map = enum_map! { Example::A => 3, _ => 0 };
///     for (_, value) in &mut map {
///         *value += 1;
///     }
///     assert_eq!(map, enum_map! { Example::A => 4, _ => 1 });
/// }
/// ```
pub struct IterMut<'a, K, V: 'a> {
    _phantom: PhantomData<K>,
    iterator: Enumerate<slice::IterMut<'a, V>>,
}

impl<'a, K: Internal<V>, V> Iterator for IterMut<'a, K, V> {
    type Item = (K, &'a mut V);
    fn next(&mut self) -> Option<Self::Item> {
        self.iterator
            .next()
            .map(|(index, item)| (K::from_usize(index), item))
    }
}

impl<'a, K: Internal<V>, V> IntoIterator for &'a mut EnumMap<K, V> {
    type Item = (K, &'a mut V);
    type IntoIter = IterMut<'a, K, V>;
    fn into_iter(self) -> Self::IntoIter {
        IterMut {
            _phantom: PhantomData,
            iterator: K::slice_mut(&mut self.array).iter_mut().enumerate(),
        }
    }
}
<commit_msg>Implement iter_mut method<commit_after>//! An enum mapping type.
//!
//! It is implemented using array, so using it is as fast as using Rust
//! arrays.

#![no_std]
#![deny(missing_docs)]

use core::hash::{Hash, Hasher};
use core::iter::Enumerate;
use core::marker::PhantomData;
use core::ops::{Index, IndexMut};
use core::slice;

// this is not stable, do not depend on this
#[doc(hidden)]
pub trait Internal<V>: Sized {
    type Array;
    fn slice(&Self::Array) -> &[V];
    fn slice_mut(&mut Self::Array) -> &mut [V];
    fn from_usize(usize) -> Self;
    fn to_usize(self) -> usize;
    fn from_function<F: Fn(Self) -> V>(F) -> Self::Array;
}

/// An enum mapping.
///
/// This internally uses an array which stores a value for each possible
/// enum value. To work, it requires implementation of internal (private,
/// although public due to macro limitations) trait which allows extracting
/// information about an enum, which can be automatically generated using
/// `#[derive(EnumMap)]` from `enum_map_derive` crate.
///
/// # Examples
///
/// ```
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// use enum_map::EnumMap;
///
/// #[derive(EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
/// }
///
/// fn main() {
///     let mut map = EnumMap::new();
///     // new initializes map with default values
///     assert_eq!(map[Example::A], 0);
///     map[Example::A] = 3;
///     assert_eq!(map[Example::A], 3);
/// }
/// ```
#[derive(Debug)]
pub struct EnumMap<K: Internal<V>, V> {
    array: K::Array,
}

impl<K: Internal<V>, V> EnumMap<K, V> {
    /// Returns an iterator over enum map.
    pub fn iter(&self) -> Iter<K, V> {
        self.into_iter()
    }

    /// Returns a mutable iterator over enum map.
    pub fn iter_mut(&mut self) -> IterMut<K, V> {
        self.into_iter()
    }
}

impl<F: Fn(K) -> V, K: Internal<V>, V> From<F> for EnumMap<K, V> {
    fn from(f: F) -> Self {
        EnumMap { array: K::from_function(f) }
    }
}

/// Enum map constructor.
///
/// This macro allows to create a new enum map in a type safe way. It takes
/// a list of `,` separated pairs separated by `=>`. Left side is `|`
/// separated list of enum keys, or `_` to match all unmatched enum keys,
/// while right side is a value.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// #[derive(EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
///     D,
/// }
///
/// fn main() {
///     let enum_map = enum_map! {
///         Example::A | Example::B => 1,
///         Example::C => 2,
///         _ => 3,
///     };
///     assert_eq!(enum_map[Example::A], 1);
///     assert_eq!(enum_map[Example::B], 1);
///     assert_eq!(enum_map[Example::C], 2);
///     assert_eq!(enum_map[Example::D], 3);
/// }
/// ```
#[macro_export]
macro_rules! enum_map {
    {$($t:tt)*} => {
        ::enum_map::EnumMap::from(|k| match k { $($t)* })
    };
}

impl<K: Internal<V>, V: Default> EnumMap<K, V>
    where K::Array: Default
{
    /// Creates an enum map with default values.
    ///
    /// ```
    /// extern crate enum_map;
    /// #[macro_use]
    /// extern crate enum_map_derive;
    ///
    /// use enum_map::EnumMap;
    ///
    /// #[derive(EnumMap)]
    /// enum Example {
    ///     A,
    /// }
    ///
    /// fn main() {
    ///     let enum_map = EnumMap::<_, i32>::new();
    ///     assert_eq!(enum_map[Example::A], 0);
    /// }
    /// ```
    pub fn new() -> Self {
        EnumMap::default()
    }
}

// Implementations provided by derive attribute are too specific, and put requirements on K.
// This is caused by rust-lang/rust#26925.
impl<K: Internal<V>, V> Clone for EnumMap<K, V>
    where K::Array: Clone
{
    fn clone(&self) -> Self {
        EnumMap { array: self.array.clone() }
    }
}

impl<K: Internal<V>, V> Copy for EnumMap<K, V> where K::Array: Copy {}

impl<K: Internal<V>, V> PartialEq for EnumMap<K, V>
    where K::Array: PartialEq
{
    fn eq(&self, other: &Self) -> bool {
        self.array == other.array
    }
}

impl<K: Internal<V>, V> Eq for EnumMap<K, V> where K::Array: Eq {}

impl<K: Internal<V>, V> Hash for EnumMap<K, V>
    where K::Array: Hash
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.array.hash(state);
    }
}

impl<K: Internal<V>, V> Default for EnumMap<K, V>
    where K::Array: Default
{
    fn default() -> Self {
        EnumMap { array: K::Array::default() }
    }
}

impl<K: Internal<V>, V> Index<K> for EnumMap<K, V> {
    type Output = V;

    fn index(&self, key: K) -> &V {
        &K::slice(&self.array)[key.to_usize()]
    }
}

impl<K: Internal<V>, V> IndexMut<K> for EnumMap<K, V> {
    fn index_mut(&mut self, key: K) -> &mut V {
        &mut K::slice_mut(&mut self.array)[key.to_usize()]
    }
}

/// Immutable enum map iterator
///
/// This struct is created by `iter` method or `into_iter` on a reference
/// to `EnumMap`.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// #[derive(EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
/// }
///
/// fn main() {
///     let mut map = enum_map! { Example::A => 3, _ => 0 };
///     assert_eq!(map[Example::A], 3);
///     for (key, &value) in &map {
///         assert_eq!(match key {
///             Example::A => 3,
///             _ => 0,
///         }, value);
///     }
/// }
/// ```
pub struct Iter<'a, K, V: 'a> {
    _phantom: PhantomData<K>,
    iterator: Enumerate<slice::Iter<'a, V>>,
}

impl<'a, K: Internal<V>, V> Iterator for Iter<'a, K, V> {
    type Item = (K, &'a V);
    fn next(&mut self) -> Option<Self::Item> {
        self.iterator
            .next()
            .map(|(index, item)| (K::from_usize(index), item))
    }
}

impl<'a, K: Internal<V>, V> IntoIterator for &'a EnumMap<K, V> {
    type Item = (K, &'a V);
    type IntoIter = Iter<'a, K, V>;
    fn into_iter(self) -> Self::IntoIter {
        Iter {
            _phantom: PhantomData,
            iterator: K::slice(&self.array).iter().enumerate(),
        }
    }
}

/// Mutable map iterator
///
/// This struct is created by `iter_mut` method or `into_iter` on a mutable
/// reference to `EnumMap`.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate enum_map;
/// #[macro_use]
/// extern crate enum_map_derive;
///
/// #[derive(Debug, EnumMap)]
/// enum Example {
///     A,
///     B,
///     C,
/// }
///
/// fn main() {
///     let mut map = enum_map! { Example::A => 3, _ => 0 };
///     for (_, value) in &mut map {
///         *value += 1;
///     }
///     assert_eq!(map, enum_map! { Example::A => 4, _ => 1 });
/// }
/// ```
pub struct IterMut<'a, K, V: 'a> {
    _phantom: PhantomData<K>,
    iterator: Enumerate<slice::IterMut<'a, V>>,
}

impl<'a, K: Internal<V>, V> Iterator for IterMut<'a, K, V> {
    type Item = (K, &'a mut V);
    fn next(&mut self) -> Option<Self::Item> {
        self.iterator
            .next()
            .map(|(index, item)| (K::from_usize(index), item))
    }
}

impl<'a, K: Internal<V>, V> IntoIterator for &'a mut EnumMap<K, V> {
    type Item = (K, &'a mut V);
    type IntoIter = IterMut<'a, K, V>;
    fn into_iter(self) -> Self::IntoIter {
        IterMut {
            _phantom: PhantomData,
            iterator: K::slice_mut(&mut self.array).iter_mut().enumerate(),
        }
    }
}
<|endoftext|>


<commit_before>use {
    Color,
    Field,
    Line,
    Matrix2d,
    RoundBorder,
};

/// A line context.
pub struct RoundBorderLineColorContext<'a> {
    /// Base/original transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current line.
    pub line: Field<'a, Line>,
    /// Current color.
    pub color: Field<'a, Color>,
    /// Current round border.
    pub round_border: Field<'a, RoundBorder>,
}

<commit_msg>Fixed doc in `RoundBorderLineColorContext`<commit_after>use {
    Color,
    Field,
    Line,
    Matrix2d,
    RoundBorder,
};

/// A line context with round border information.
pub struct RoundBorderLineColorContext<'a> {
    /// Base/original transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current line.
    pub line: Field<'a, Line>,
    /// Current color.
    pub color: Field<'a, Color>,
    /// Current round border.
    pub round_border: Field<'a, RoundBorder>,
}

<|endoftext|>


<commit_before>//
// min-llvm-version: 10.0.1
// compile-flags: -O
#![crate_type="lib"]

#[no_mangle]
pub fn alloc_test(data: u32) {
    // CHECK-LABEL: @alloc_test
    // CHECK-NEXT: start:
    // CHECK-NEXT: ret void
    let x = Box::new(data);
    drop(x);
}
<commit_msg>fix: alloc-optimisation is only for rust llvm<commit_after>//
// no-system-llvm
// min-llvm-version: 10.0.1
// compile-flags: -O
#![crate_type="lib"]

#[no_mangle]
pub fn alloc_test(data: u32) {
    // CHECK-LABEL: @alloc_test
    // CHECK-NEXT: start:
    // CHECK-NEXT: ret void
    let x = Box::new(data);
    drop(x);
}
<|endoftext|>