skytool 0.1.0-pre.2

an experimental API client for BlueSky / ATProto
Documentation
use std::str::FromStr;

use json_patch::Patch as JsonPatch;
use serde_json::Value as JsonValue;

pub(crate) const LONG_HELP: &str = "
Accepts both RFC6902 (JSON Patch) and RFC7396 (JSON Merge Patch) syntax.";

#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[error(transparent)]
#[remain::sorted]
pub(crate) enum Error {
  Json(#[from] serde_json::Error),
  Patch(#[from] json_patch::PatchError),
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(untagged)]
pub(crate) enum Patch {
  RFC6902(JsonPatch),
  RFC7396(JsonValue),
}

impl Patch {
  pub(crate) fn apply<'doc>(&self, doc: &'doc mut JsonValue) -> Result<&'doc mut JsonValue, Error> {
    match self {
      Self::RFC6902(patch) => json_patch::patch(doc, patch)?,
      Self::RFC7396(patch) => json_patch::merge(doc, patch),
    }

    Ok(doc)
  }
}

impl From<JsonPatch> for Patch {
  fn from(patch: JsonPatch) -> Self {
    Self::RFC6902(patch)
  }
}

impl From<JsonValue> for Patch {
  fn from(value: JsonValue) -> Self {
    if value.is_array() {
      match serde_json::from_value(value.clone()) {
        Ok(patch) => Self::RFC6902(patch),
        Err(_) => Self::RFC7396(value),
      }
    } else {
      Self::RFC7396(value)
    }
  }
}

impl FromStr for Patch {
  type Err = Error;
  fn from_str(s: &str) -> Result<Self, Self::Err> {
    Ok(serde_json::from_str::<JsonValue>(s)?.into())
  }
}