<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use chrono::naive::datetime::NaiveDateTime;
use toml_query::delete::TomlValueDeleteExt;
use toml_query::insert::TomlValueInsertExt;
use toml_query::read::TomlValueReadExt;
use toml::Value;

use libimagstore::store::Entry;
use libimagerror::into::IntoError;

use error::DateErrorKind as DEK;
use error::*;
use result::Result;
use range::DateTimeRange;

pub trait EntryDate {

    fn delete_date(&mut self) -> Result<()>;
    fn read_date(&self) -> Result<NaiveDateTime>;
    fn set_date(&mut self, d: NaiveDateTime) -> Result<Option<Result<NaiveDateTime>>>;

    fn delete_date_range(&mut self) -> Result<()>;
    fn read_date_range(&self) -> Result<DateTimeRange>;
    fn set_date_range(&mut self, start: NaiveDateTime, end: NaiveDateTime) -> Result<Option<Result<DateTimeRange>>>;

}

lazy_static! {
    static ref DATE_HEADER_LOCATION : &'static str              = "date.value";
    static ref DATE_RANGE_START_HEADER_LOCATION : &'static str  = "date.range.start";
    static ref DATE_RANGE_END_HEADER_LOCATION : &'static str    = "date.range.end";
    static ref DATE_FMT : &'static str                          = "%Y-%m-%dT%H:%M:%S";
}

impl EntryDate for Entry {

    fn delete_date(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(&DATE_HEADER_LOCATION)
            .map(|_| ())
            .map_err_into(DEK::DeleteDateError)
    }

    fn read_date(&self) -> Result<NaiveDateTime> {
        self.get_header()
            .read(&DATE_HEADER_LOCATION)
            .map_err_into(DEK::ReadDateError)
            .and_then(|v| {
                match v {
                    &Value::String(ref s) => s.parse::<NaiveDateTime>()
                        .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            })
    }

    /// Set a Date for this entry
    ///
    /// # Return value
    ///
    /// This function returns funny things, I know. But I find it more attractive to be explicit
    /// what failed when here, instead of beeing nice to the user here.
    ///
    /// So here's a list how things are returned:
    ///
    /// - Err(_) if the inserting failed
    /// - Ok(None) if the inserting succeeded and _did not replace an existing value_.
    /// - Ok(Some(Ok(_))) if the inserting succeeded, but replaced an existing value which then got
    /// parsed into a NaiveDateTime object
    /// - Ok(Some(Err(_))) if the inserting succeeded, but replaced an existing value which then
    /// got parsed into a NaiveDateTime object, where the parsing failed for some reason.
    ///
    fn set_date(&mut self, d: NaiveDateTime) -> Result<Option<Result<NaiveDateTime>>> {
        let date = d.format(&DATE_FMT).to_string();

        self.get_header_mut()
            .insert(&DATE_HEADER_LOCATION, Value::String(date))
            .map(|opt| opt.map(|stri| {
                match stri {
                    Value::String(ref s) => s.parse::<NaiveDateTime>()
                                             .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }))
            .map_err_into(DEK::SetDateError)
    }

    /// Deletes the date range
    ///
    /// # Warning
    ///
    /// First deletes the start, then the end. If the first operation fails, this might leave the
    /// header in an inconsistent state.
    ///
    fn delete_date_range(&mut self) -> Result<()> {
        let _ = try!(self
             .get_header_mut()
            .delete(&DATE_RANGE_START_HEADER_LOCATION)
            .map(|_| ())
            .map_err_into(DEK::DeleteDateTimeRangeError));

        self.get_header_mut()
            .delete(&DATE_RANGE_END_HEADER_LOCATION)
            .map(|_| ())
            .map_err_into(DEK::DeleteDateTimeRangeError)
    }

    fn read_date_range(&self) -> Result<DateTimeRange> {
        let start = try!(self
            .get_header()
            .read(&DATE_RANGE_START_HEADER_LOCATION)
            .map_err_into(DEK::ReadDateTimeRangeError)
            .and_then(|v| {
                match v {
                    &Value::String(ref s) => s.parse::<NaiveDateTime>()
                        .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }));

        let end = try!(self
            .get_header()
            .read(&DATE_RANGE_START_HEADER_LOCATION)
            .map_err_into(DEK::ReadDateTimeRangeError)
            .and_then(|v| {
                match v {
                    &Value::String(ref s) => s.parse::<NaiveDateTime>()
                        .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }));

        DateTimeRange::new(start, end)
            .map_err_into(DEK::DateTimeRangeError)
    }

    /// Set the date range
    ///
    /// # Warning
    ///
    /// This first sets the start, then the end. If the first operation fails, this might leave the
    /// header in an inconsistent state.
    ///
    fn set_date_range(&mut self, start: NaiveDateTime, end: NaiveDateTime)
        -> Result<Option<Result<DateTimeRange>>>
    {
        let start = start.format(&DATE_FMT).to_string();
        let end   = end.format(&DATE_FMT).to_string();

        let opt_old_start = try!(self
            .get_header_mut()
            .insert(&DATE_RANGE_START_HEADER_LOCATION, Value::String(start))
            .map(|opt| opt.map(|stri| {
                match stri {
                    Value::String(ref s) => s.parse::<NaiveDateTime>()
                                             .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }))
            .map_err_into(DEK::SetDateTimeRangeError));

        let opt_old_end = try!(self
            .get_header_mut()
            .insert(&DATE_RANGE_END_HEADER_LOCATION, Value::String(end))
            .map(|opt| opt.map(|stri| {
                match stri {
                    Value::String(ref s) => s.parse::<NaiveDateTime>()
                                             .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }))
            .map_err_into(DEK::SetDateTimeRangeError));

        match (opt_old_start, opt_old_end) {
            (Some(Ok(old_start)), Some(Ok(old_end))) => {
                let dr = DateTimeRange::new(old_start, old_end)
                    .map_err_into(DEK::DateTimeRangeError);

                Ok(Some(dr))
            },

            (Some(Err(e)), _) => Err(e),
            (_, Some(Err(e))) => Err(e),
            _ => {
                Ok(None)
            },
        }
    }

}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use super::*;

    use libimagstore::store::Store;

    use chrono::naive::datetime::NaiveDateTime;
    use chrono::naive::date::NaiveDate;
    use chrono::naive::time::NaiveTime;

    pub fn get_store() -> Store {
        Store::new(PathBuf::from("/"), None).unwrap()
    }

    #[test]
    fn test_set_date() {
        let store = get_store();

        let date = {
            let date = NaiveDate::from_ymd(2000, 01, 02);
            let time = NaiveTime::from_hms(03, 04, 05);

            NaiveDateTime::new(date, time)
        };

        let mut entry   = store.create(PathBuf::from("test")).unwrap();
        let res         = entry.set_date(date);

        assert!(res.is_ok(), format!("Error: {:?}", res));
        let res = res.unwrap();

        assert!(res.is_none()); // There shouldn't be an existing value

        // Check whether the header is set correctly

        let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);

        assert!(hdr_field.is_ok());
        let hdr_field = hdr_field.unwrap();

        match *hdr_field {
            Value::String(ref s) => assert_eq!("2000-01-02T03:04:05", s),
            _ => assert!(false, "Wrong header type"),
        }
    }

    #[test]
    fn test_read_date() {
        use chrono::Datelike;
        use chrono::Timelike;

        let store = get_store();

        let date = {
            let date = NaiveDate::from_ymd(2000, 01, 02);
            let time = NaiveTime::from_hms(03, 04, 05);

            NaiveDateTime::new(date, time)
        };

        let mut entry   = store.create(PathBuf::from("test")).unwrap();
        let res         = entry.set_date(date);

        assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
        let res = res.unwrap();

        assert!(res.is_none()); // There shouldn't be an existing value

        // same as the test above ...

        let d = entry.read_date();

        assert!(d.is_ok(), format!("Expected Ok(_), got: {:?}", d));
        let d = d.unwrap();

        assert_eq!(d.date().year()   , 2000);
        assert_eq!(d.date().month()  ,   01);
        assert_eq!(d.date().day()    ,   02);
        assert_eq!(d.time().hour()   ,   03);
        assert_eq!(d.time().minute() ,   04);
        assert_eq!(d.time().second() ,   05);
    }

    #[test]
    fn test_delete_date() {
        let store = get_store();

        let date = {
            let date = NaiveDate::from_ymd(2000, 01, 02);
            let time = NaiveTime::from_hms(03, 04, 05);

            NaiveDateTime::new(date, time)
        };

        let mut entry   = store.create(PathBuf::from("test")).unwrap();
        let res         = entry.set_date(date);

        assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
        let res = res.unwrap();
        assert!(res.is_none()); // There shouldn't be an existing value

        assert!(entry.delete_date().is_ok());

        let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);

        assert!(hdr_field.is_err(), format!("Expected Err(_), got: {:?}", hdr_field));
    }
}

<commit_msg>Fix header location for datetime values<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use chrono::naive::datetime::NaiveDateTime;
use toml_query::delete::TomlValueDeleteExt;
use toml_query::insert::TomlValueInsertExt;
use toml_query::read::TomlValueReadExt;
use toml::Value;

use libimagstore::store::Entry;
use libimagerror::into::IntoError;

use error::DateErrorKind as DEK;
use error::*;
use result::Result;
use range::DateTimeRange;

pub trait EntryDate {

    fn delete_date(&mut self) -> Result<()>;
    fn read_date(&self) -> Result<NaiveDateTime>;
    fn set_date(&mut self, d: NaiveDateTime) -> Result<Option<Result<NaiveDateTime>>>;

    fn delete_date_range(&mut self) -> Result<()>;
    fn read_date_range(&self) -> Result<DateTimeRange>;
    fn set_date_range(&mut self, start: NaiveDateTime, end: NaiveDateTime) -> Result<Option<Result<DateTimeRange>>>;

}

lazy_static! {
    static ref DATE_HEADER_LOCATION : &'static str              = "datetime.value";
    static ref DATE_RANGE_START_HEADER_LOCATION : &'static str  = "datetime.range.start";
    static ref DATE_RANGE_END_HEADER_LOCATION : &'static str    = "datetime.range.end";
    static ref DATE_FMT : &'static str                          = "%Y-%m-%dT%H:%M:%S";
}

impl EntryDate for Entry {

    fn delete_date(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(&DATE_HEADER_LOCATION)
            .map(|_| ())
            .map_err_into(DEK::DeleteDateError)
    }

    fn read_date(&self) -> Result<NaiveDateTime> {
        self.get_header()
            .read(&DATE_HEADER_LOCATION)
            .map_err_into(DEK::ReadDateError)
            .and_then(|v| {
                match v {
                    &Value::String(ref s) => s.parse::<NaiveDateTime>()
                        .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            })
    }

    /// Set a Date for this entry
    ///
    /// # Return value
    ///
    /// This function returns funny things, I know. But I find it more attractive to be explicit
    /// what failed when here, instead of beeing nice to the user here.
    ///
    /// So here's a list how things are returned:
    ///
    /// - Err(_) if the inserting failed
    /// - Ok(None) if the inserting succeeded and _did not replace an existing value_.
    /// - Ok(Some(Ok(_))) if the inserting succeeded, but replaced an existing value which then got
    /// parsed into a NaiveDateTime object
    /// - Ok(Some(Err(_))) if the inserting succeeded, but replaced an existing value which then
    /// got parsed into a NaiveDateTime object, where the parsing failed for some reason.
    ///
    fn set_date(&mut self, d: NaiveDateTime) -> Result<Option<Result<NaiveDateTime>>> {
        let date = d.format(&DATE_FMT).to_string();

        self.get_header_mut()
            .insert(&DATE_HEADER_LOCATION, Value::String(date))
            .map(|opt| opt.map(|stri| {
                match stri {
                    Value::String(ref s) => s.parse::<NaiveDateTime>()
                                             .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }))
            .map_err_into(DEK::SetDateError)
    }

    /// Deletes the date range
    ///
    /// # Warning
    ///
    /// First deletes the start, then the end. If the first operation fails, this might leave the
    /// header in an inconsistent state.
    ///
    fn delete_date_range(&mut self) -> Result<()> {
        let _ = try!(self
             .get_header_mut()
            .delete(&DATE_RANGE_START_HEADER_LOCATION)
            .map(|_| ())
            .map_err_into(DEK::DeleteDateTimeRangeError));

        self.get_header_mut()
            .delete(&DATE_RANGE_END_HEADER_LOCATION)
            .map(|_| ())
            .map_err_into(DEK::DeleteDateTimeRangeError)
    }

    fn read_date_range(&self) -> Result<DateTimeRange> {
        let start = try!(self
            .get_header()
            .read(&DATE_RANGE_START_HEADER_LOCATION)
            .map_err_into(DEK::ReadDateTimeRangeError)
            .and_then(|v| {
                match v {
                    &Value::String(ref s) => s.parse::<NaiveDateTime>()
                        .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }));

        let end = try!(self
            .get_header()
            .read(&DATE_RANGE_START_HEADER_LOCATION)
            .map_err_into(DEK::ReadDateTimeRangeError)
            .and_then(|v| {
                match v {
                    &Value::String(ref s) => s.parse::<NaiveDateTime>()
                        .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }));

        DateTimeRange::new(start, end)
            .map_err_into(DEK::DateTimeRangeError)
    }

    /// Set the date range
    ///
    /// # Warning
    ///
    /// This first sets the start, then the end. If the first operation fails, this might leave the
    /// header in an inconsistent state.
    ///
    fn set_date_range(&mut self, start: NaiveDateTime, end: NaiveDateTime)
        -> Result<Option<Result<DateTimeRange>>>
    {
        let start = start.format(&DATE_FMT).to_string();
        let end   = end.format(&DATE_FMT).to_string();

        let opt_old_start = try!(self
            .get_header_mut()
            .insert(&DATE_RANGE_START_HEADER_LOCATION, Value::String(start))
            .map(|opt| opt.map(|stri| {
                match stri {
                    Value::String(ref s) => s.parse::<NaiveDateTime>()
                                             .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }))
            .map_err_into(DEK::SetDateTimeRangeError));

        let opt_old_end = try!(self
            .get_header_mut()
            .insert(&DATE_RANGE_END_HEADER_LOCATION, Value::String(end))
            .map(|opt| opt.map(|stri| {
                match stri {
                    Value::String(ref s) => s.parse::<NaiveDateTime>()
                                             .map_err_into(DEK::DateTimeParsingError),
                    _ => Err(DEK::DateHeaderFieldTypeError.into_error()),
                }
            }))
            .map_err_into(DEK::SetDateTimeRangeError));

        match (opt_old_start, opt_old_end) {
            (Some(Ok(old_start)), Some(Ok(old_end))) => {
                let dr = DateTimeRange::new(old_start, old_end)
                    .map_err_into(DEK::DateTimeRangeError);

                Ok(Some(dr))
            },

            (Some(Err(e)), _) => Err(e),
            (_, Some(Err(e))) => Err(e),
            _ => {
                Ok(None)
            },
        }
    }

}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use super::*;

    use libimagstore::store::Store;

    use chrono::naive::datetime::NaiveDateTime;
    use chrono::naive::date::NaiveDate;
    use chrono::naive::time::NaiveTime;

    pub fn get_store() -> Store {
        Store::new(PathBuf::from("/"), None).unwrap()
    }

    #[test]
    fn test_set_date() {
        let store = get_store();

        let date = {
            let date = NaiveDate::from_ymd(2000, 01, 02);
            let time = NaiveTime::from_hms(03, 04, 05);

            NaiveDateTime::new(date, time)
        };

        let mut entry   = store.create(PathBuf::from("test")).unwrap();
        let res         = entry.set_date(date);

        assert!(res.is_ok(), format!("Error: {:?}", res));
        let res = res.unwrap();

        assert!(res.is_none()); // There shouldn't be an existing value

        // Check whether the header is set correctly

        let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);

        assert!(hdr_field.is_ok());
        let hdr_field = hdr_field.unwrap();

        match *hdr_field {
            Value::String(ref s) => assert_eq!("2000-01-02T03:04:05", s),
            _ => assert!(false, "Wrong header type"),
        }
    }

    #[test]
    fn test_read_date() {
        use chrono::Datelike;
        use chrono::Timelike;

        let store = get_store();

        let date = {
            let date = NaiveDate::from_ymd(2000, 01, 02);
            let time = NaiveTime::from_hms(03, 04, 05);

            NaiveDateTime::new(date, time)
        };

        let mut entry   = store.create(PathBuf::from("test")).unwrap();
        let res         = entry.set_date(date);

        assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
        let res = res.unwrap();

        assert!(res.is_none()); // There shouldn't be an existing value

        // same as the test above ...

        let d = entry.read_date();

        assert!(d.is_ok(), format!("Expected Ok(_), got: {:?}", d));
        let d = d.unwrap();

        assert_eq!(d.date().year()   , 2000);
        assert_eq!(d.date().month()  ,   01);
        assert_eq!(d.date().day()    ,   02);
        assert_eq!(d.time().hour()   ,   03);
        assert_eq!(d.time().minute() ,   04);
        assert_eq!(d.time().second() ,   05);
    }

    #[test]
    fn test_delete_date() {
        let store = get_store();

        let date = {
            let date = NaiveDate::from_ymd(2000, 01, 02);
            let time = NaiveTime::from_hms(03, 04, 05);

            NaiveDateTime::new(date, time)
        };

        let mut entry   = store.create(PathBuf::from("test")).unwrap();
        let res         = entry.set_date(date);

        assert!(res.is_ok(), format!("Expected Ok(_), got: {:?}", res));
        let res = res.unwrap();
        assert!(res.is_none()); // There shouldn't be an existing value

        assert!(entry.delete_date().is_ok());

        let hdr_field = entry.get_header().read(&DATE_HEADER_LOCATION);

        assert!(hdr_field.is_err(), format!("Expected Err(_), got: {:?}", hdr_field));
    }
}

<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;

use chrono::naive::datetime::NaiveDateTime as NDT;

use constants::*;
use error::TimeTrackError;
use error::TimeTrackErrorKind as TTEK;
use error::MapErrInto;
use iter::tag::TagIter;
use iter::create::CreateTimeTrackIter;

use libimagstore::store::Store;
use libimagstore::storeid::StoreId;

pub struct TagStoreIdIter {
    inner: TagIter,
    datetime: NDT,
}

impl TagStoreIdIter {

    pub fn new(inner: TagIter, datetime: NDT) -> TagStoreIdIter {
        TagStoreIdIter {
            inner: inner,
            datetime: datetime,
        }
    }

    pub fn create_entries<'a>(self, store: &'a Store) -> CreateTimeTrackIter<'a> {
        CreateTimeTrackIter::new(self, store)
    }

}

impl Iterator for TagStoreIdIter {
    type Item = Result<(StoreId, NDT), TimeTrackError>;

    fn next(&mut self) -> Option<Self::Item> {
        self.inner
            .next()
            .map(|res| {
                res.and_then(|tag| {
                    let dt = self.datetime.format(DATE_TIME_FORMAT).to_string();
                    let id_str = format!("{}-{}", dt, tag.as_str());
                    let id_str = PathBuf::from(id_str);

                    StoreId::new_baseless(id_str)
                        .map_err_into(TTEK::StoreIdError)
                        .map(|id| (
                            id,
                            self.datetime.clone()
                        ))
                })
            })
    }
}

<commit_msg>Redo StoreId creation in iterator<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;

use chrono::naive::datetime::NaiveDateTime as NDT;

use constants::*;
use error::TimeTrackError;
use error::TimeTrackErrorKind as TTEK;
use error::MapErrInto;
use iter::tag::TagIter;
use iter::create::CreateTimeTrackIter;

use libimagstore::store::Store;
use libimagstore::storeid::StoreId;

pub struct TagStoreIdIter {
    inner: TagIter,
    datetime: NDT,
}

impl TagStoreIdIter {

    pub fn new(inner: TagIter, datetime: NDT) -> TagStoreIdIter {
        TagStoreIdIter {
            inner: inner,
            datetime: datetime,
        }
    }

    pub fn create_entries<'a>(self, store: &'a Store) -> CreateTimeTrackIter<'a> {
        CreateTimeTrackIter::new(self, store)
    }

}

impl Iterator for TagStoreIdIter {
    type Item = Result<(StoreId, NDT), TimeTrackError>;

    fn next(&mut self) -> Option<Self::Item> {
        use module_path::ModuleEntryPath;
        use libimagstore::storeid::IntoStoreId;

        self.inner
            .next()
            .map(|res| {
                res.and_then(|tag| {
                    let dt = self.datetime.format(DATE_TIME_FORMAT).to_string();
                    let id_str = format!("{}-{}", dt, tag.as_str());
                    ModuleEntryPath::new(id_str)
                        .into_storeid()
                        .map_err_into(TTEK::StoreIdError)
                        .map(|id| (id, self.datetime.clone()))
                })
            })
    }
}

<|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 http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use std::io::Write;

use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Layout};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
    AnnotationSet, Cfg, ConditionWrite, Documentation, GenericPath, Path, PrimitiveType,
    ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::utilities::{find_first_some, IterHelpers};
use crate::bindgen::writer::{Source, SourceWriter};

#[derive(Debug, Clone)]
pub struct FunctionArgument {
    pub name: Option<String>,
    pub ty: Type,
    pub array_length: Option<String>,
}

#[derive(Debug, Clone)]
pub struct Function {
    pub path: Path,
    /// Path to the self-type of the function
    /// If the function is a method, this will contain the path of the type in the impl block
    pub self_type_path: Option<Path>,
    pub ret: Type,
    pub args: Vec<FunctionArgument>,
    pub extern_decl: bool,
    pub cfg: Option<Cfg>,
    pub annotations: AnnotationSet,
    pub documentation: Documentation,
    pub never_return: bool,
}

impl Function {
    pub fn load(
        path: Path,
        self_type_path: Option<&Path>,
        sig: &syn::Signature,
        extern_decl: bool,
        attrs: &[syn::Attribute],
        mod_cfg: Option<&Cfg>,
    ) -> Result<Function, String> {
        let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?;

        let mut never_return = false;
        let mut ret = match sig.output {
            syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
            syn::ReturnType::Type(_, ref ty) => {
                if let syn::Type::Never(_) = ty.as_ref() {
                    never_return = true;
                    Type::Primitive(PrimitiveType::Void)
                } else {
                    Type::load(ty)?.unwrap_or_else(|| Type::Primitive(PrimitiveType::Void))
                }
            }
        };

        if let Some(self_path) = self_type_path {
            for arg in &mut args {
                arg.ty.replace_self_with(self_path);
            }
            ret.replace_self_with(self_path);
        }

        Ok(Function {
            path,
            self_type_path: self_type_path.cloned(),
            ret,
            args,
            extern_decl,
            cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
            annotations: AnnotationSet::load(attrs)?,
            documentation: Documentation::load(attrs),
            never_return,
        })
    }

    pub fn swift_name(&self) -> Option<String> {
        // If the symbol name starts with the type name, separate the two components with '.'
        // so that Swift recognises the association between the method and the type
        let (ref type_prefix, ref type_name) = match self.self_type_path {
            Some(ref type_name) => {
                let type_name = type_name.to_string();
                if !self.path.name().starts_with(&type_name) {
                    return Some(self.path.to_string());
                }
                (format!("{}.", type_name), type_name)
            }
            None => ("".to_string(), "".to_string()),
        };

        let item_name = self
            .path
            .name()
            .trim_start_matches(type_name)
            .trim_start_matches('_');

        let item_args = {
            let mut items = Vec::with_capacity(self.args.len());
            for arg in self.args.iter() {
                items.push(format!("{}:", arg.name.as_ref()?.as_str()));
            }
            items.join("")
        };
        Some(format!("{}{}({})", type_prefix, item_name, item_args))
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn simplify_standard_types(&mut self) {
        self.ret.simplify_standard_types();
        for arg in &mut self.args {
            arg.ty.simplify_standard_types();
        }
    }

    pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
        self.ret.add_dependencies(library, out);
        for arg in &self.args {
            arg.ty.add_dependencies(library, out);
        }
    }

    pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
        self.ret.add_monomorphs(library, out);
        for arg in &self.args {
            arg.ty.add_monomorphs(library, out);
        }
    }

    pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
        self.ret.mangle_paths(monomorphs);
        for arg in &mut self.args {
            arg.ty.mangle_paths(monomorphs);
        }
    }

    pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
        self.ret.resolve_declaration_types(resolver);
        for arg in &mut self.args {
            arg.ty.resolve_declaration_types(resolver);
        }
    }

    pub fn rename_for_config(&mut self, config: &Config) {
        // Rename the types used in arguments
        let generic_params = Default::default();
        self.ret.rename_for_config(config, &generic_params);

        // Apply rename rules to argument names
        let rules = [
            self.annotations.parse_atom::<RenameRule>("rename-all"),
            config.function.rename_args,
        ];

        if let Some(r) = find_first_some(&rules) {
            let args = std::mem::replace(&mut self.args, vec![]);
            self.args = args
                .into_iter()
                .map(|arg| {
                    let name = arg.name.map(|n| r.apply(&n, IdentifierType::FunctionArg));
                    FunctionArgument {
                        name,
                        ty: arg.ty,
                        array_length: None,
                    }
                })
                .collect()
        }

        // Escape C/C++ reserved keywords used in argument names, and
        // recursively rename argument types.
        for arg in &mut self.args {
            arg.ty.rename_for_config(config, &generic_params);
            if let Some(ref mut name) = arg.name {
                reserved::escape(name);
            }
        }

        // Save the array length of the pointer arguments which need to use
        // the C-array notation
        if let Some(tuples) = self.annotations.list("ptrs-as-arrays") {
            let mut ptrs_as_arrays: HashMap<String, String> = HashMap::new();
            for str_tuple in tuples {
                let parts: Vec<&str> = str_tuple[1..str_tuple.len() - 1]
                    .split(';')
                    .map(|x| x.trim())
                    .collect();
                if parts.len() != 2 {
                    warn!(
                        "{:?} does not follow the correct syntax, so the annotation is being ignored",
                        parts
                    );
                    continue;
                }
                ptrs_as_arrays.insert(parts[0].to_string(), parts[1].to_string());
            }

            for arg in &mut self.args {
                match arg.ty {
                    Type::Ptr { .. } => {},
                    _ => continue,
                }
                let name = match arg.name {
                    Some(ref name) => name,
                    None => continue,
                };
                arg.array_length = ptrs_as_arrays.get(name).cloned();
            }
        }
    }
}

impl Source for Function {
    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
        fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
            let prefix = config.function.prefix(&func.annotations);
            let postfix = config.function.postfix(&func.annotations);

            let condition = func.cfg.to_condition(config);
            condition.write_before(config, out);

            func.documentation.write(config, out);

            if func.extern_decl {
                out.write("extern ");
            } else {
                if let Some(ref prefix) = prefix {
                    write!(out, "{} ", prefix);
                }
                if func.annotations.must_use {
                    if let Some(ref anno) = config.function.must_use {
                        write!(out, "{} ", anno);
                    }
                }
            }
            cdecl::write_func(out, &func, false, config);

            if !func.extern_decl {
                if let Some(ref postfix) = postfix {
                    write!(out, " {}", postfix);
                }
            }

            if let Some(ref swift_name_macro) = config.function.swift_name_macro {
                if let Some(swift_name) = func.swift_name() {
                    write!(out, " {}({})", swift_name_macro, swift_name);
                }
            }

            if func.never_return {
                if let Some(ref no_return_attr) = config.function.no_return {
                    out.write_fmt(format_args!(" {}", no_return_attr));
                }
            }

            out.write(";");

            condition.write_after(config, out);
        }

        fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
            let prefix = config.function.prefix(&func.annotations);
            let postfix = config.function.postfix(&func.annotations);

            let condition = func.cfg.to_condition(config);

            condition.write_before(config, out);

            func.documentation.write(config, out);

            if func.extern_decl {
                out.write("extern ");
            } else {
                if let Some(ref prefix) = prefix {
                    write!(out, "{}", prefix);
                    out.new_line();
                }
                if func.annotations.must_use {
                    if let Some(ref anno) = config.function.must_use {
                        write!(out, "{}", anno);
                        out.new_line();
                    }
                }
            }
            cdecl::write_func(out, &func, true, config);
            if !func.extern_decl {
                if let Some(ref postfix) = postfix {
                    out.new_line();
                    write!(out, "{}", postfix);
                }
            }

            if let Some(ref swift_name_macro) = config.function.swift_name_macro {
                if let Some(swift_name) = func.swift_name() {
                    write!(out, " {}({})", swift_name_macro, swift_name);
                }
            }

            if func.never_return {
                if let Some(ref no_return_attr) = config.function.no_return {
                    out.write_fmt(format_args!(" {}", no_return_attr));
                }
            }

            out.write(";");

            condition.write_after(config, out);
        };

        let option_1 = out.measure(|out| write_1(self, config, out));

        if (config.function.args == Layout::Auto && option_1 <= config.line_length)
            || config.function.args == Layout::Horizontal
        {
            write_1(self, config, out);
        } else {
            write_2(self, config, out);
        }
    }
}

trait SynFnArgHelpers {
    fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
}

fn gen_self_type(receiver: &syn::Receiver) -> Type {
    let self_ty = Type::Path(GenericPath::self_path());
    if receiver.reference.is_none() {
        return self_ty;
    }

    let is_const = receiver.mutability.is_none();
    Type::Ptr {
        ty: Box::new(self_ty),
        is_const,
        is_nullable: false,
        is_ref: false,
    }
}

impl SynFnArgHelpers for syn::FnArg {
    fn as_argument(&self) -> Result<Option<FunctionArgument>, String> {
        match *self {
            syn::FnArg::Typed(syn::PatType {
                ref pat, ref ty, ..
            }) => {
                let name = match **pat {
                    syn::Pat::Wild(..) => None,
                    syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => Some(ident.to_string()),
                    _ => {
                        return Err(format!(
                            "Parameter has an unsupported argument name: {:?}",
                            pat
                        ))
                    }
                };
                let ty = match Type::load(ty)? {
                    Some(x) => x,
                    None => return Ok(None),
                };
                if let Type::Array(..) = ty {
                    return Err("Array as function arguments are not supported".to_owned());
                }
                Ok(Some(FunctionArgument {
                    name,
                    ty,
                    array_length: None,
                }))
            }
            syn::FnArg::Receiver(ref receiver) => Ok(Some(FunctionArgument {
                name: Some("self".to_string()),
                ty: gen_self_type(receiver),
                array_length: None,
            })),
        }
    }
}
<commit_msg>Appease rustfmt.<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 http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use std::io::Write;

use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Layout};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
    AnnotationSet, Cfg, ConditionWrite, Documentation, GenericPath, Path, PrimitiveType,
    ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::utilities::{find_first_some, IterHelpers};
use crate::bindgen::writer::{Source, SourceWriter};

#[derive(Debug, Clone)]
pub struct FunctionArgument {
    pub name: Option<String>,
    pub ty: Type,
    pub array_length: Option<String>,
}

#[derive(Debug, Clone)]
pub struct Function {
    pub path: Path,
    /// Path to the self-type of the function
    /// If the function is a method, this will contain the path of the type in the impl block
    pub self_type_path: Option<Path>,
    pub ret: Type,
    pub args: Vec<FunctionArgument>,
    pub extern_decl: bool,
    pub cfg: Option<Cfg>,
    pub annotations: AnnotationSet,
    pub documentation: Documentation,
    pub never_return: bool,
}

impl Function {
    pub fn load(
        path: Path,
        self_type_path: Option<&Path>,
        sig: &syn::Signature,
        extern_decl: bool,
        attrs: &[syn::Attribute],
        mod_cfg: Option<&Cfg>,
    ) -> Result<Function, String> {
        let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?;

        let mut never_return = false;
        let mut ret = match sig.output {
            syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
            syn::ReturnType::Type(_, ref ty) => {
                if let syn::Type::Never(_) = ty.as_ref() {
                    never_return = true;
                    Type::Primitive(PrimitiveType::Void)
                } else {
                    Type::load(ty)?.unwrap_or_else(|| Type::Primitive(PrimitiveType::Void))
                }
            }
        };

        if let Some(self_path) = self_type_path {
            for arg in &mut args {
                arg.ty.replace_self_with(self_path);
            }
            ret.replace_self_with(self_path);
        }

        Ok(Function {
            path,
            self_type_path: self_type_path.cloned(),
            ret,
            args,
            extern_decl,
            cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
            annotations: AnnotationSet::load(attrs)?,
            documentation: Documentation::load(attrs),
            never_return,
        })
    }

    pub fn swift_name(&self) -> Option<String> {
        // If the symbol name starts with the type name, separate the two components with '.'
        // so that Swift recognises the association between the method and the type
        let (ref type_prefix, ref type_name) = match self.self_type_path {
            Some(ref type_name) => {
                let type_name = type_name.to_string();
                if !self.path.name().starts_with(&type_name) {
                    return Some(self.path.to_string());
                }
                (format!("{}.", type_name), type_name)
            }
            None => ("".to_string(), "".to_string()),
        };

        let item_name = self
            .path
            .name()
            .trim_start_matches(type_name)
            .trim_start_matches('_');

        let item_args = {
            let mut items = Vec::with_capacity(self.args.len());
            for arg in self.args.iter() {
                items.push(format!("{}:", arg.name.as_ref()?.as_str()));
            }
            items.join("")
        };
        Some(format!("{}{}({})", type_prefix, item_name, item_args))
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn simplify_standard_types(&mut self) {
        self.ret.simplify_standard_types();
        for arg in &mut self.args {
            arg.ty.simplify_standard_types();
        }
    }

    pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
        self.ret.add_dependencies(library, out);
        for arg in &self.args {
            arg.ty.add_dependencies(library, out);
        }
    }

    pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
        self.ret.add_monomorphs(library, out);
        for arg in &self.args {
            arg.ty.add_monomorphs(library, out);
        }
    }

    pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
        self.ret.mangle_paths(monomorphs);
        for arg in &mut self.args {
            arg.ty.mangle_paths(monomorphs);
        }
    }

    pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
        self.ret.resolve_declaration_types(resolver);
        for arg in &mut self.args {
            arg.ty.resolve_declaration_types(resolver);
        }
    }

    pub fn rename_for_config(&mut self, config: &Config) {
        // Rename the types used in arguments
        let generic_params = Default::default();
        self.ret.rename_for_config(config, &generic_params);

        // Apply rename rules to argument names
        let rules = [
            self.annotations.parse_atom::<RenameRule>("rename-all"),
            config.function.rename_args,
        ];

        if let Some(r) = find_first_some(&rules) {
            let args = std::mem::replace(&mut self.args, vec![]);
            self.args = args
                .into_iter()
                .map(|arg| {
                    let name = arg.name.map(|n| r.apply(&n, IdentifierType::FunctionArg));
                    FunctionArgument {
                        name,
                        ty: arg.ty,
                        array_length: None,
                    }
                })
                .collect()
        }

        // Escape C/C++ reserved keywords used in argument names, and
        // recursively rename argument types.
        for arg in &mut self.args {
            arg.ty.rename_for_config(config, &generic_params);
            if let Some(ref mut name) = arg.name {
                reserved::escape(name);
            }
        }

        // Save the array length of the pointer arguments which need to use
        // the C-array notation
        if let Some(tuples) = self.annotations.list("ptrs-as-arrays") {
            let mut ptrs_as_arrays: HashMap<String, String> = HashMap::new();
            for str_tuple in tuples {
                let parts: Vec<&str> = str_tuple[1..str_tuple.len() - 1]
                    .split(';')
                    .map(|x| x.trim())
                    .collect();
                if parts.len() != 2 {
                    warn!(
                        "{:?} does not follow the correct syntax, so the annotation is being ignored",
                        parts
                    );
                    continue;
                }
                ptrs_as_arrays.insert(parts[0].to_string(), parts[1].to_string());
            }

            for arg in &mut self.args {
                match arg.ty {
                    Type::Ptr { .. } => {}
                    _ => continue,
                }
                let name = match arg.name {
                    Some(ref name) => name,
                    None => continue,
                };
                arg.array_length = ptrs_as_arrays.get(name).cloned();
            }
        }
    }
}

impl Source for Function {
    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
        fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
            let prefix = config.function.prefix(&func.annotations);
            let postfix = config.function.postfix(&func.annotations);

            let condition = func.cfg.to_condition(config);
            condition.write_before(config, out);

            func.documentation.write(config, out);

            if func.extern_decl {
                out.write("extern ");
            } else {
                if let Some(ref prefix) = prefix {
                    write!(out, "{} ", prefix);
                }
                if func.annotations.must_use {
                    if let Some(ref anno) = config.function.must_use {
                        write!(out, "{} ", anno);
                    }
                }
            }
            cdecl::write_func(out, &func, false, config);

            if !func.extern_decl {
                if let Some(ref postfix) = postfix {
                    write!(out, " {}", postfix);
                }
            }

            if let Some(ref swift_name_macro) = config.function.swift_name_macro {
                if let Some(swift_name) = func.swift_name() {
                    write!(out, " {}({})", swift_name_macro, swift_name);
                }
            }

            if func.never_return {
                if let Some(ref no_return_attr) = config.function.no_return {
                    out.write_fmt(format_args!(" {}", no_return_attr));
                }
            }

            out.write(";");

            condition.write_after(config, out);
        }

        fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
            let prefix = config.function.prefix(&func.annotations);
            let postfix = config.function.postfix(&func.annotations);

            let condition = func.cfg.to_condition(config);

            condition.write_before(config, out);

            func.documentation.write(config, out);

            if func.extern_decl {
                out.write("extern ");
            } else {
                if let Some(ref prefix) = prefix {
                    write!(out, "{}", prefix);
                    out.new_line();
                }
                if func.annotations.must_use {
                    if let Some(ref anno) = config.function.must_use {
                        write!(out, "{}", anno);
                        out.new_line();
                    }
                }
            }
            cdecl::write_func(out, &func, true, config);
            if !func.extern_decl {
                if let Some(ref postfix) = postfix {
                    out.new_line();
                    write!(out, "{}", postfix);
                }
            }

            if let Some(ref swift_name_macro) = config.function.swift_name_macro {
                if let Some(swift_name) = func.swift_name() {
                    write!(out, " {}({})", swift_name_macro, swift_name);
                }
            }

            if func.never_return {
                if let Some(ref no_return_attr) = config.function.no_return {
                    out.write_fmt(format_args!(" {}", no_return_attr));
                }
            }

            out.write(";");

            condition.write_after(config, out);
        };

        let option_1 = out.measure(|out| write_1(self, config, out));

        if (config.function.args == Layout::Auto && option_1 <= config.line_length)
            || config.function.args == Layout::Horizontal
        {
            write_1(self, config, out);
        } else {
            write_2(self, config, out);
        }
    }
}

trait SynFnArgHelpers {
    fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
}

fn gen_self_type(receiver: &syn::Receiver) -> Type {
    let self_ty = Type::Path(GenericPath::self_path());
    if receiver.reference.is_none() {
        return self_ty;
    }

    let is_const = receiver.mutability.is_none();
    Type::Ptr {
        ty: Box::new(self_ty),
        is_const,
        is_nullable: false,
        is_ref: false,
    }
}

impl SynFnArgHelpers for syn::FnArg {
    fn as_argument(&self) -> Result<Option<FunctionArgument>, String> {
        match *self {
            syn::FnArg::Typed(syn::PatType {
                ref pat, ref ty, ..
            }) => {
                let name = match **pat {
                    syn::Pat::Wild(..) => None,
                    syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => Some(ident.to_string()),
                    _ => {
                        return Err(format!(
                            "Parameter has an unsupported argument name: {:?}",
                            pat
                        ))
                    }
                };
                let ty = match Type::load(ty)? {
                    Some(x) => x,
                    None => return Ok(None),
                };
                if let Type::Array(..) = ty {
                    return Err("Array as function arguments are not supported".to_owned());
                }
                Ok(Some(FunctionArgument {
                    name,
                    ty,
                    array_length: None,
                }))
            }
            syn::FnArg::Receiver(ref receiver) => Ok(Some(FunctionArgument {
                name: Some("self".to_string()),
                ty: gen_self_type(receiver),
                array_length: None,
            })),
        }
    }
}
<|endoftext|>


<commit_before>mod common;

use crate::common::service;

use snowchains::app::{
    App, Opt, OutputKind, Retrieve, RetrieveLanguages, RetrieveSubmissions, Submit,
};
use snowchains::config;
use snowchains::errors::{ServiceError, ServiceErrorKind};
use snowchains::service::ServiceKind;
use snowchains::terminal::{AnsiColorChoice, Dumb, TtyOrPiped};

use difference::assert_diff;
use failure::Fallible;
use if_chain::if_chain;
use once_cell::sync::Lazy;
use pretty_assertions::assert_eq;
use regex::Regex;

use std::convert::TryFrom as _;

#[test]
fn it_logins() -> Fallible<()> {
    fn login(app: App<TtyOrPiped<&[u8]>, Dumb, Dumb>) -> Fallible<()> {
        service::login(app, ServiceKind::Yukicoder).map_err(Into::into)
    }

    let stdin = format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?);
    service::test_in_tempdir("it_logins", &stdin, login)
}

#[test]
fn it_scrapes_and_downloads_testcases() -> Fallible<()> {
    service::test_in_tempdir(
        "it_downloads_test_cases_from_master",
        &format!("Y\n{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |app| -> _ {
            static CONTEST: &str = "no";
            let wd = app.working_dir.clone();
            service::retrieve_testcases(
                app,
                ServiceKind::Yukicoder,
                CONTEST,
                &["3", "725", "726"],
            )?;
            service::confirm_num_cases(
                &wd,
                ServiceKind::Yukicoder,
                CONTEST,
                &[("3", 31), ("725", 9), ("726", 25)],
            )
        },
    )
}

#[test]
fn retrieve_submissions_command_works_without_error() -> Fallible<()> {
    service::test_in_tempdir(
        "retrieve_submissions_command_works_without_error",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            let code = app.run(Opt::Retrieve(Retrieve::Submissions(RetrieveSubmissions {
                fetch_all: false,
                no_save: true,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                mode: config::Mode::Debug,
                problems: vec!["1".to_owned()],
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
            })))?;
            assert_eq!(code, 0);
            let stdout = String::try_from(app.stdout)?;
            let stderr = String::try_from(app.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            assert!(stderr.starts_with(
                &r#"
GET https://yukicoder.me/api/v1/problems ... 200 OK
GET https://yukicoder.me/api/v1/languages ... 200 OK
GET https://yukicoder.me/problems/no/1/submissions?my_submission=enabled ... 200 OK
"#[1..],
            ));
            Ok(())
        },
    )
}

#[test]
fn it_fails_to_submit_if_the_lang_name_is_invalid() -> Fallible<()> {
    service::test_in_tempdir(
        "it_fails_to_submit_if_the_lang_name_is_invalid",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            static CODE: &[u8] = b"#";
            let dir = app.working_dir.join("yukicoder").join("no").join("py");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.py"), CODE)?;
            let err = app
                .run(Opt::Submit(Submit {
                    open: false,
                    force_compile: false,
                    only_transpile: false,
                    no_judge: true,
                    debug: false,
                    no_check_duplication: false,
                    verbose: false,
                    json: false,
                    colorize: false,
                    service: Some(ServiceKind::Yukicoder),
                    contest: Some("no".to_owned()),
                    language: Some("python3-with-invalid-lang-names".to_owned()),
                    mode: config::Mode::Release,
                    jobs: None,
                    output: OutputKind::Pretty,
                    color_choice: AnsiColorChoice::Never,
                    problem: "9000".to_owned(),
                }))
                .unwrap_err();
            if_chain! {
                if let snowchains::Error::Service(ServiceError::Context(ctx)) = &err;
                if let ServiceErrorKind::NoSuchLang(lang_name) = ctx.get_context();
                then {
                    assert_eq!(lang_name, "invalid");
                    Ok(())
                } else {
                    Err(err.into())
                }
            }
        },
    )
}

#[test]
#[ignore]
fn it_submits_to_no_9000() -> Fallible<()> {
    service::test_in_tempdir(
        "it_submits_to_no_9000",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            static CODE: &[u8] = b"Hello World!\n";
            let dir = app.working_dir.join("yukicoder").join("no").join("txt");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.txt"), CODE)?;
            let code = app.run(Opt::Submit(Submit {
                open: false,
                force_compile: false,
                only_transpile: false,
                no_judge: true,
                debug: false,
                no_check_duplication: false,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                language: Some("text".to_owned()),
                mode: config::Mode::Release,
                jobs: None,
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
                problem: "9000".to_owned(),
            }))?;
            assert_eq!(code, 0);
            let stdout = String::try_from(app.stdout)?;
            String::try_from(app.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            Ok(())
        },
    )
}

#[test]
fn it_retrieves_languages() -> Fallible<()> {
    service::test_in_tempdir(
        "it_retrieves_languages",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            static MASK_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new("Username: .*").unwrap());

            let code = app.run(Opt::Retrieve(Retrieve::Languages(RetrieveLanguages {
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                output: OutputKind::Pretty,
                color_choice: AnsiColorChoice::Never,
                problem: Some("9000".to_owned()),
            })))?;
            assert_eq!(code, 0);
            let stdout = String::try_from(app.stdout)?;
            let stderr = String::try_from(app.stderr)?;
            assert_diff!(
                &stdout,
                &r#"
┌──────────────────────────────────────────────┬─────────────┐
│ Name                                         │ ID          │
├──────────────────────────────────────────────┼─────────────┤
│ C++11 (gcc 4.8.5)                            │ cpp         │
├──────────────────────────────────────────────┼─────────────┤
│ C++14 (gcc 8.3.0)                            │ cpp14       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(1z) (gcc 8.3.0)                        │ cpp17       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(clang Beta) (clang 7.0.0)              │ cpp-clang   │
├──────────────────────────────────────────────┼─────────────┤
│ C (gcc 8.3.0)                                │ c11         │
├──────────────────────────────────────────────┼─────────────┤
│ C90 (gcc 4.8.5)                              │ c           │
├──────────────────────────────────────────────┼─────────────┤
│ Java8 (openjdk 1.8.0.222)                    │ java8       │
├──────────────────────────────────────────────┼─────────────┤
│ C# (csc 3.100.19.26603)                      │ csharp      │
├──────────────────────────────────────────────┼─────────────┤
│ C#(mono) (mono 6.0.0.327)                    │ csharp_mono │
├──────────────────────────────────────────────┼─────────────┤
│ Perl (5.16.3)                                │ perl        │
├──────────────────────────────────────────────┼─────────────┤
│ Perl6 (rakudo 2019.07.1-303-g59e683f)        │ perl6       │
├──────────────────────────────────────────────┼─────────────┤
│ PHP (5.4.16)                                 │ php         │
├──────────────────────────────────────────────┼─────────────┤
│ PHP7 (7.3.9)                                 │ php7        │
├──────────────────────────────────────────────┼─────────────┤
│ Python2 (2.7.16)                             │ python      │
├──────────────────────────────────────────────┼─────────────┤
│ Python3 (3.7.4 + numpy 1.14.5 + scipy 1.1.0) │ python3     │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy2 (7.1.1)                                │ pypy2       │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy3 (7.0.0)                                │ pypy3       │
├──────────────────────────────────────────────┼─────────────┤
│ Ruby (2.6.4p104)                             │ ruby        │
├──────────────────────────────────────────────┼─────────────┤
│ D (dmd 2.088.0)                              │ d           │
├──────────────────────────────────────────────┼─────────────┤
│ Go (1.13)                                    │ go          │
├──────────────────────────────────────────────┼─────────────┤
│ Haskell (8.8.1)                              │ haskell     │
├──────────────────────────────────────────────┼─────────────┤
│ Scala(Beta) (2.13.0)                         │ scala       │
├──────────────────────────────────────────────┼─────────────┤
│ Nim (0.20.99)                                │ nim         │
├──────────────────────────────────────────────┼─────────────┤
│ Rust (1.37.0)                                │ rust        │
├──────────────────────────────────────────────┼─────────────┤
│ Kotlin (1.3.10)                              │ kotlin      │
├──────────────────────────────────────────────┼─────────────┤
│ Scheme (Gauche-0.9.8)                        │ scheme      │
├──────────────────────────────────────────────┼─────────────┤
│ Crystal (0.30.1)                             │ crystal     │
├──────────────────────────────────────────────┼─────────────┤
│ Swift(Beta) (5.0.3)                          │ swift       │
├──────────────────────────────────────────────┼─────────────┤
│ OCaml (4.05.0)                               │ ocaml       │
├──────────────────────────────────────────────┼─────────────┤
│ Clojure(Beta) (1.10.1.469)                   │ clojure     │
├──────────────────────────────────────────────┼─────────────┤
│ F# (4.5)                                     │ fsharp      │
├──────────────────────────────────────────────┼─────────────┤
│ Elixir (1.10.0-dev)                          │ elixir      │
├──────────────────────────────────────────────┼─────────────┤
│ Lua (LuaJit 2.0.5)                           │ lua         │
├──────────────────────────────────────────────┼─────────────┤
│ Fortran (gFortran 4.8.5)                     │ fortran     │
├──────────────────────────────────────────────┼─────────────┤
│ JavaScript (node v12.9.1)                    │ node        │
├──────────────────────────────────────────────┼─────────────┤
│ Common Lisp (sbcl 1.5.5)                     │ lisp        │
├──────────────────────────────────────────────┼─────────────┤
│ Kuin(beta) (v.2019.9.17 on Wine)             │ kuin        │
├──────────────────────────────────────────────┼─────────────┤
│ Vim script (v8.1.127)                        │ vim         │
├──────────────────────────────────────────────┼─────────────┤
│ Bash (Bash 4.2.46)                           │ sh          │
├──────────────────────────────────────────────┼─────────────┤
│ Text (cat 8.22)                              │ text        │
├──────────────────────────────────────────────┼─────────────┤
│ Assembler (nasm 2.10.07)                     │ nasm        │
├──────────────────────────────────────────────┼─────────────┤
│ cLay (20190921-1)                            │ clay        │
├──────────────────────────────────────────────┼─────────────┤
│ Brainfuck (BFI 1.1)                          │ bf          │
├──────────────────────────────────────────────┼─────────────┤
│ Whitespace (0.3)                             │ Whitespace  │
└──────────────────────────────────────────────┴─────────────┘
"#[1..],
                "\n",
                0
            );
            assert_diff!(
                &MASK_USERNAME.replace(&stderr, "Username: ██████████"),
                &r#"
GET https://yukicoder.me/ ... 200 OK

Input "REVEL_SESSION".

Chrome: chrome://settings/cookies/detail?site=yukicoder.me&search=cookie
Firefox: sqlite3 "$YOUR_FIREFOX_PROFILE/cookies.sqlite" 'SELECT value FROM moz_cookies WHERE baseDomain="yukicoder.me" AND name="REVEL_SESSION"'

REVEL_SESSION: GET https://yukicoder.me/ ... 200 OK
Confirmed.
Username: ██████████
GET https://yukicoder.me/problems/no/9000/submit ... 200 OK

"#[1..],
                "\n",
                0
            );
            Ok(())
        },
    )
}
<commit_msg>Update a language list<commit_after>mod common;

use crate::common::service;

use snowchains::app::{
    App, Opt, OutputKind, Retrieve, RetrieveLanguages, RetrieveSubmissions, Submit,
};
use snowchains::config;
use snowchains::errors::{ServiceError, ServiceErrorKind};
use snowchains::service::ServiceKind;
use snowchains::terminal::{AnsiColorChoice, Dumb, TtyOrPiped};

use difference::assert_diff;
use failure::Fallible;
use if_chain::if_chain;
use once_cell::sync::Lazy;
use pretty_assertions::assert_eq;
use regex::Regex;

use std::convert::TryFrom as _;

#[test]
fn it_logins() -> Fallible<()> {
    fn login(app: App<TtyOrPiped<&[u8]>, Dumb, Dumb>) -> Fallible<()> {
        service::login(app, ServiceKind::Yukicoder).map_err(Into::into)
    }

    let stdin = format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?);
    service::test_in_tempdir("it_logins", &stdin, login)
}

#[test]
fn it_scrapes_and_downloads_testcases() -> Fallible<()> {
    service::test_in_tempdir(
        "it_downloads_test_cases_from_master",
        &format!("Y\n{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |app| -> _ {
            static CONTEST: &str = "no";
            let wd = app.working_dir.clone();
            service::retrieve_testcases(
                app,
                ServiceKind::Yukicoder,
                CONTEST,
                &["3", "725", "726"],
            )?;
            service::confirm_num_cases(
                &wd,
                ServiceKind::Yukicoder,
                CONTEST,
                &[("3", 31), ("725", 9), ("726", 25)],
            )
        },
    )
}

#[test]
fn retrieve_submissions_command_works_without_error() -> Fallible<()> {
    service::test_in_tempdir(
        "retrieve_submissions_command_works_without_error",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            let code = app.run(Opt::Retrieve(Retrieve::Submissions(RetrieveSubmissions {
                fetch_all: false,
                no_save: true,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                mode: config::Mode::Debug,
                problems: vec!["1".to_owned()],
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
            })))?;
            assert_eq!(code, 0);
            let stdout = String::try_from(app.stdout)?;
            let stderr = String::try_from(app.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            assert!(stderr.starts_with(
                &r#"
GET https://yukicoder.me/api/v1/problems ... 200 OK
GET https://yukicoder.me/api/v1/languages ... 200 OK
GET https://yukicoder.me/problems/no/1/submissions?my_submission=enabled ... 200 OK
"#[1..],
            ));
            Ok(())
        },
    )
}

#[test]
fn it_fails_to_submit_if_the_lang_name_is_invalid() -> Fallible<()> {
    service::test_in_tempdir(
        "it_fails_to_submit_if_the_lang_name_is_invalid",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            static CODE: &[u8] = b"#";
            let dir = app.working_dir.join("yukicoder").join("no").join("py");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.py"), CODE)?;
            let err = app
                .run(Opt::Submit(Submit {
                    open: false,
                    force_compile: false,
                    only_transpile: false,
                    no_judge: true,
                    debug: false,
                    no_check_duplication: false,
                    verbose: false,
                    json: false,
                    colorize: false,
                    service: Some(ServiceKind::Yukicoder),
                    contest: Some("no".to_owned()),
                    language: Some("python3-with-invalid-lang-names".to_owned()),
                    mode: config::Mode::Release,
                    jobs: None,
                    output: OutputKind::Pretty,
                    color_choice: AnsiColorChoice::Never,
                    problem: "9000".to_owned(),
                }))
                .unwrap_err();
            if_chain! {
                if let snowchains::Error::Service(ServiceError::Context(ctx)) = &err;
                if let ServiceErrorKind::NoSuchLang(lang_name) = ctx.get_context();
                then {
                    assert_eq!(lang_name, "invalid");
                    Ok(())
                } else {
                    Err(err.into())
                }
            }
        },
    )
}

#[test]
#[ignore]
fn it_submits_to_no_9000() -> Fallible<()> {
    service::test_in_tempdir(
        "it_submits_to_no_9000",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            static CODE: &[u8] = b"Hello World!\n";
            let dir = app.working_dir.join("yukicoder").join("no").join("txt");
            std::fs::create_dir_all(&dir)?;
            std::fs::write(&dir.join("9000.txt"), CODE)?;
            let code = app.run(Opt::Submit(Submit {
                open: false,
                force_compile: false,
                only_transpile: false,
                no_judge: true,
                debug: false,
                no_check_duplication: false,
                verbose: false,
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                language: Some("text".to_owned()),
                mode: config::Mode::Release,
                jobs: None,
                output: OutputKind::Json,
                color_choice: AnsiColorChoice::Never,
                problem: "9000".to_owned(),
            }))?;
            assert_eq!(code, 0);
            let stdout = String::try_from(app.stdout)?;
            String::try_from(app.stderr)?;
            serde_json::from_str::<serde_json::Value>(&stdout)?;
            Ok(())
        },
    )
}

#[test]
fn it_retrieves_languages() -> Fallible<()> {
    service::test_in_tempdir(
        "it_retrieves_languages",
        &format!("{}\n", service::env_var("YUKICODER_REVEL_SESSION")?),
        |mut app| -> _ {
            static MASK_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new("Username: .*").unwrap());

            let code = app.run(Opt::Retrieve(Retrieve::Languages(RetrieveLanguages {
                json: false,
                colorize: false,
                service: Some(ServiceKind::Yukicoder),
                contest: Some("no".to_owned()),
                output: OutputKind::Pretty,
                color_choice: AnsiColorChoice::Never,
                problem: Some("9000".to_owned()),
            })))?;
            assert_eq!(code, 0);
            let stdout = String::try_from(app.stdout)?;
            let stderr = String::try_from(app.stderr)?;
            assert_diff!(
                &r#"
┌──────────────────────────────────────────────┬─────────────┐
│ Name                                         │ ID          │
├──────────────────────────────────────────────┼─────────────┤
│ C++14 (gcc 9.2.0)                            │ cpp14       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(1z) (gcc 9.2.0)                        │ cpp17       │
├──────────────────────────────────────────────┼─────────────┤
│ C++17(clang Beta) (clang 7.0.0)              │ cpp-clang   │
├──────────────────────────────────────────────┼─────────────┤
│ C++11 (gcc 4.8.5)                            │ cpp         │
├──────────────────────────────────────────────┼─────────────┤
│ C (gcc 9.2.0)                                │ c11         │
├──────────────────────────────────────────────┼─────────────┤
│ C90 (gcc 4.8.5)                              │ c           │
├──────────────────────────────────────────────┼─────────────┤
│ Java11 (openjdk 11.0.5)                      │ java8       │
├──────────────────────────────────────────────┼─────────────┤
│ C# (csc 3.4.0-beta4-19569-03)                │ csharp      │
├──────────────────────────────────────────────┼─────────────┤
│ C#(mono) (mono 6.6.0.161)                    │ csharp_mono │
├──────────────────────────────────────────────┼─────────────┤
│ Perl (5.16.3)                                │ perl        │
├──────────────────────────────────────────────┼─────────────┤
│ Perl6 (rakudo 2019.11-303-g5c65a12)          │ perl6       │
├──────────────────────────────────────────────┼─────────────┤
│ PHP (5.4.16)                                 │ php         │
├──────────────────────────────────────────────┼─────────────┤
│ PHP7 (7.4.1)                                 │ php7        │
├──────────────────────────────────────────────┼─────────────┤
│ Python2 (2.7.17)                             │ python      │
├──────────────────────────────────────────────┼─────────────┤
│ Python3 (3.8.1 + numpy 1.14.5 + scipy 1.1.0) │ python3     │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy2 (7.1.1)                                │ pypy2       │
├──────────────────────────────────────────────┼─────────────┤
│ PyPy3 (7.0.0)                                │ pypy3       │
├──────────────────────────────────────────────┼─────────────┤
│ Ruby (2.7.0p0)                               │ ruby        │
├──────────────────────────────────────────────┼─────────────┤
│ D (dmd 2.089.1)                              │ d           │
├──────────────────────────────────────────────┼─────────────┤
│ Go (1.13.5)                                  │ go          │
├──────────────────────────────────────────────┼─────────────┤
│ Haskell (8.8.1)                              │ haskell     │
├──────────────────────────────────────────────┼─────────────┤
│ Scala(Beta) (2.13.1)                         │ scala       │
├──────────────────────────────────────────────┼─────────────┤
│ Nim (1.1.1)                                  │ nim         │
├──────────────────────────────────────────────┼─────────────┤
│ Rust (1.40.0)                                │ rust        │
├──────────────────────────────────────────────┼─────────────┤
│ Kotlin (1.3.10)                              │ kotlin      │
├──────────────────────────────────────────────┼─────────────┤
│ Scheme (Gauche-0.9.9)                        │ scheme      │
├──────────────────────────────────────────────┼─────────────┤
│ Crystal (0.32.4)                             │ crystal     │
├──────────────────────────────────────────────┼─────────────┤
│ Swift(Beta) (5.1.3)                          │ swift       │
├──────────────────────────────────────────────┼─────────────┤
│ OCaml (4.05.0)                               │ ocaml       │
├──────────────────────────────────────────────┼─────────────┤
│ Clojure(Beta) (1.10.1.469)                   │ clojure     │
├──────────────────────────────────────────────┼─────────────┤
│ F# (4.5)                                     │ fsharp      │
├──────────────────────────────────────────────┼─────────────┤
│ Elixir (1.10.0-dev)                          │ elixir      │
├──────────────────────────────────────────────┼─────────────┤
│ Lua (LuaJit 2.0.5)                           │ lua         │
├──────────────────────────────────────────────┼─────────────┤
│ Fortran (gFortran 4.8.5)                     │ fortran     │
├──────────────────────────────────────────────┼─────────────┤
│ JavaScript (node v13.5.0)                    │ node        │
├──────────────────────────────────────────────┼─────────────┤
│ Common Lisp (sbcl 1.5.5)                     │ lisp        │
├──────────────────────────────────────────────┼─────────────┤
│ Kuin(beta) (v.2019.9.17 on Wine)             │ kuin        │
├──────────────────────────────────────────────┼─────────────┤
│ Vim script (v8.1.127)                        │ vim         │
├──────────────────────────────────────────────┼─────────────┤
│ Bash (Bash 4.2.46)                           │ sh          │
├──────────────────────────────────────────────┼─────────────┤
│ Text (cat 8.22)                              │ text        │
├──────────────────────────────────────────────┼─────────────┤
│ Assembler (nasm 2.10.07)                     │ nasm        │
├──────────────────────────────────────────────┼─────────────┤
│ cLay (20191227-1)                            │ clay        │
├──────────────────────────────────────────────┼─────────────┤
│ Brainfuck (BFI 1.1)                          │ bf          │
├──────────────────────────────────────────────┼─────────────┤
│ Whitespace (0.3)                             │ Whitespace  │
└──────────────────────────────────────────────┴─────────────┘
"#[1..],
                &stdout,
                "\n",
                0
            );
            assert_diff!(
                &MASK_USERNAME.replace(&stderr, "Username: ██████████"),
                &r#"
GET https://yukicoder.me/ ... 200 OK

Input "REVEL_SESSION".

Chrome: chrome://settings/cookies/detail?site=yukicoder.me&search=cookie
Firefox: sqlite3 "$YOUR_FIREFOX_PROFILE/cookies.sqlite" 'SELECT value FROM moz_cookies WHERE baseDomain="yukicoder.me" AND name="REVEL_SESSION"'

REVEL_SESSION: GET https://yukicoder.me/ ... 200 OK
Confirmed.
Username: ██████████
GET https://yukicoder.me/problems/no/9000/submit ... 200 OK

"#[1..],
                "\n",
                0
            );
            Ok(())
        },
    )
}
<|endoftext|>


<commit_before><commit_msg>Don't spin the event loop<commit_after><|endoftext|>


<commit_before>use crate::command_prelude::*;
use cargo::ops::{self, OutputMetadataOptions};

pub fn cli() -> App {
    subcommand("metadata")
        .about(
            "Output the resolved dependencies of a package, \
             the concrete used versions including overrides, \
             in machine-readable format",
        )
        .arg(opt("quiet", "No output printed to stdout").short("q"))
        .arg_features()
        .arg(multi_opt(
            "filter-platform",
            "TRIPLE",
            "Only include resolve dependencies matching the given target-triple",
        ))
        .arg(opt(
            "no-deps",
            "Output information only about the workspace members \
             and don't fetch dependencies",
        ))
        .arg_manifest_path()
        .arg(
            opt("format-version", "Format version")
                .value_name("VERSION")
                .possible_value("1"),
        )
        .after_help("Run `cargo help metadata` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    let ws = args.workspace(config)?;

    let version = match args.value_of("format-version") {
        None => {
            config.shell().warn(
                "please specify `--format-version` flag explicitly \
                 to avoid compatibility problems",
            )?;
            1
        }
        Some(version) => version.parse().unwrap(),
    };

    let options = OutputMetadataOptions {
        features: values(args, "features"),
        all_features: args.is_present("all-features"),
        no_default_features: args.is_present("no-default-features"),
        no_deps: args.is_present("no-deps"),
        filter_platforms: args._values_of("filter-platform"),
        version,
    };

    let result = ops::output_metadata(&ws, &options)?;
    config.shell().print_json(&result);
    Ok(())
}
<commit_msg>Update display message for cargo metadata<commit_after>use crate::command_prelude::*;
use cargo::ops::{self, OutputMetadataOptions};

pub fn cli() -> App {
    subcommand("metadata")
        .about(
            "Output the resolved dependencies of a package, \
             the concrete used versions including overrides, \
             in machine-readable format",
        )
        .arg(opt("quiet", "Do not print cargo log messages").short("q"))
        .arg_features()
        .arg(multi_opt(
            "filter-platform",
            "TRIPLE",
            "Only include resolve dependencies matching the given target-triple",
        ))
        .arg(opt(
            "no-deps",
            "Output information only about the workspace members \
             and don't fetch dependencies",
        ))
        .arg_manifest_path()
        .arg(
            opt("format-version", "Format version")
                .value_name("VERSION")
                .possible_value("1"),
        )
        .after_help("Run `cargo help metadata` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    let ws = args.workspace(config)?;

    let version = match args.value_of("format-version") {
        None => {
            config.shell().warn(
                "please specify `--format-version` flag explicitly \
                 to avoid compatibility problems",
            )?;
            1
        }
        Some(version) => version.parse().unwrap(),
    };

    let options = OutputMetadataOptions {
        features: values(args, "features"),
        all_features: args.is_present("all-features"),
        no_default_features: args.is_present("no-default-features"),
        no_deps: args.is_present("no-deps"),
        filter_platforms: args._values_of("filter-platform"),
        version,
    };

    let result = ops::output_metadata(&ws, &options)?;
    config.shell().print_json(&result);
    Ok(())
}
<|endoftext|>