tutel 0.2.10

a minimalistic todo app for terminal enthusiasts
Documentation
use serde::{
    de::{self, Visitor},
    Deserialize,
};

use super::{project::ProjectData, Task};

const PROJECT_DATA_FIELDS: &[&str] = &["name", "tasks"];

enum ProjectDataField {
    Name,
    Tasks,
}

struct ProjectDataFieldVisitor;

impl<'de> Visitor<'de> for ProjectDataFieldVisitor {
    type Value = ProjectDataField;

    fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str("'name' or 'tasks'")
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        match v {
            "name" => Ok(ProjectDataField::Name),
            "tasks" => Ok(ProjectDataField::Tasks),
            _ => Err(de::Error::unknown_field(v, PROJECT_DATA_FIELDS)),
        }
    }
}

impl<'de> Deserialize<'de> for ProjectDataField {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_identifier(ProjectDataFieldVisitor)
    }
}

struct ProjectDataVisitor;

impl<'de> Visitor<'de> for ProjectDataVisitor {
    type Value = ProjectData;

    fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str("struct ProjectData")
    }

    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: de::MapAccess<'de>,
    {
        let mut name = None;
        let mut tasks = None;
        while let Some(key) = map.next_key()? {
            match key {
                ProjectDataField::Name => {
                    if name.is_some() {
                        return Err(de::Error::duplicate_field("name"));
                    }
                    name = Some(map.next_value()?);
                }
                ProjectDataField::Tasks => {
                    if tasks.is_some() {
                        return Err(de::Error::duplicate_field("tasks"));
                    }
                    tasks = Some(map.next_value()?);
                }
            }
        }

        let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
        let tasks = tasks.ok_or_else(|| de::Error::missing_field("tasks"))?;

        Ok(ProjectData { name, tasks })
    }
}

impl<'de> Deserialize<'de> for ProjectData {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_struct("ProjectData", PROJECT_DATA_FIELDS, ProjectDataVisitor)
    }
}

const TASK_FIELDS: &[&str] = &["name", "index", "completed"];

enum TaskField {
    Name,
    Index,
    Completed,
}

struct TaskFieldVisitor;

impl<'de> Visitor<'de> for TaskFieldVisitor {
    type Value = TaskField;

    fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str("'name', 'index' or 'completed'")
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        match v {
            "name" | "desc" => Ok(TaskField::Name),
            "index" => Ok(TaskField::Index),
            "completed" => Ok(TaskField::Completed),
            _ => Err(de::Error::unknown_field(v, TASK_FIELDS)),
        }
    }
}

impl<'de> Deserialize<'de> for TaskField {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_identifier(TaskFieldVisitor)
    }
}

struct TaskVisitor;

impl<'de> Visitor<'de> for TaskVisitor {
    type Value = Task;

    fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str("struct Task")
    }

    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: de::MapAccess<'de>,
    {
        let mut description = None;
        let mut index = None;
        let mut completed = None;
        while let Some(key) = map.next_key()? {
            match key {
                TaskField::Name => {
                    if description.is_some() {
                        return Err(de::Error::duplicate_field("desc/name"));
                    }
                    description = Some(map.next_value()?);
                }
                TaskField::Index => {
                    if index.is_some() {
                        return Err(de::Error::duplicate_field("index"));
                    }
                    index = Some(map.next_value()?);
                }
                TaskField::Completed => {
                    if completed.is_some() {
                        return Err(de::Error::duplicate_field("completed"));
                    }
                    completed = Some(map.next_value()?);
                }
            }
        }

        let desc = description.ok_or_else(|| de::Error::missing_field("desc or name"))?;
        let index = index.ok_or_else(|| de::Error::missing_field("index"))?;
        let completed = completed.ok_or_else(|| de::Error::missing_field("completed"))?;

        Ok(Task {
            desc,
            index,
            completed,
        })
    }
}

impl<'de> Deserialize<'de> for Task {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_struct("Task", TASK_FIELDS, TaskVisitor)
    }
}