itchio-api 0.3.0

Easily interact with the itch.io server-side API
Documentation
use std::fmt;
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
use serde::{Deserialize, Deserializer, de::{self, Visitor}};

struct DateVisitor;

impl<'de> Visitor<'de> for DateVisitor {
  type Value = DateTime<Utc>;

  fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    formatter.write_str("a string for a date in format 1900-01-01 00:00:00(.000000)")
  }

  fn visit_str<E>(self, value: &str) -> Result<DateTime<Utc>, E>
  where
    E: de::Error,
  {
    // Early dates are in format "%F %T%.f", less early dates are in format "%F %T". The latter case somehow doesn't fail.
    // There's a semi-expectation of a remainder so that more unexpected cases don't cause a ParseError(TooLong).
    let (date, _remainder) = NaiveDateTime::parse_and_remainder(value, "%F %T%.f")
      .map_err(|e| {
        eprintln!("Unable to format {value}: {:?}", e);
        de::Error::custom(e)
      })?;
    Ok(date.and_utc())
  }
}

pub fn date_from_str<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
  D: Deserializer<'de>,
{
  deserializer.deserialize_identifier(DateVisitor) // unsure if that's the right function but it works so oh well
}

struct OptionDateVisitor;

impl<'de> Visitor<'de> for OptionDateVisitor {
  type Value = Option<DateTime<Utc>>;

  fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    formatter.write_str("a string for a date in format 1900-01-01 00:00:00(.000000) or null (undefined)")
  }

  fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E>
  where
    E: de::Error,
  {
    Ok(None)
  }

  fn visit_some<D>(self, deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
  where
    D: Deserializer<'de>,
  {
    deserializer.deserialize_str(DateVisitor).map(Some)
  }
}

pub fn option_date_from_str<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
  D: Deserializer<'de>,
{
  deserializer.deserialize_option(OptionDateVisitor)
}


struct SmallDateVisitor;

impl<'de> Visitor<'de> for SmallDateVisitor {
  type Value = DateTime<Utc>;

  fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    formatter.write_str("a string for a date in format 1900-01-01)")
  }

  fn visit_str<E>(self, value: &str) -> Result<DateTime<Utc>, E>
  where
    E: de::Error,
  {
    let (date, _remainder) = NaiveDate::parse_and_remainder(value, "%F")
      .map_err(|e| {
        eprintln!("Unable to format {value}: {:?}", e);
        de::Error::custom(e)
      })?;
    Ok(date.and_time(NaiveTime::default()).and_utc())
  }
}

pub fn small_date_from_str<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
  D: Deserializer<'de>,
{
  deserializer.deserialize_identifier(SmallDateVisitor) // unsure if that's the right function but it works so oh well
}

/// Needed because of https://github.com/itchio/itch.io/issues/1301
///
/// I must admit it's in part AI slop because I don't understand deserializer well yet,
/// TODO understand this code and make it better
pub fn empty_object_as_empty_array<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
where
  T: Deserialize<'de>,
  D: Deserializer<'de>,
{
  struct EmptyObjectAsEmptyArrayVisitor<T> {
    marker: std::marker::PhantomData<T>,
  }

  impl<'de, T> Visitor<'de> for EmptyObjectAsEmptyArrayVisitor<T>
  where
    T: Deserialize<'de>,
  {
    type Value = Vec<T>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
      formatter.write_str("an empty object or an array")
    }

    fn visit_unit<E>(self) -> Result<Self::Value, E>
    where
      E: de::Error,
    {
      Ok(Vec::new())
    }

    fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
    where
      M: serde::de::MapAccess<'de>,
    {
      if map.next_key::<T>()?.is_none() {
        Ok(Vec::new())
      } else {
        Err(de::Error::custom("expected an empty object"))
      }
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
      A: serde::de::SeqAccess<'de>,
    {
      let mut vec = Vec::new();
      while let Some(item) = seq.next_element()? {
        vec.push(item);
      }
      Ok(vec)
    }
  }

  deserializer.deserialize_any(EmptyObjectAsEmptyArrayVisitor {
    marker: std::marker::PhantomData,
  })
}