use core::fmt;
use core::marker::PhantomData;
use std::collections::BTreeMap;
use blazingly_json::Value;
use serde::de::{DeserializeSeed, Deserializer, IgnoredAny, MapAccess, SeqAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize, Serializer, de};
use crate::{
model::{Completeness, EditPlan, FileEdit, TextEdit},
provenance::Provenance,
};
pub(crate) const TEXT_EDIT_FIELDS: [&str; 7] = [
"startLine",
"startChar",
"endLine",
"endChar",
"before",
"after",
"provenance",
];
pub(crate) const FILE_EDIT_FIELDS: [&str; 3] = ["path", "sha256", "edits"];
pub(crate) const EDIT_PLAN_FIELDS: [&str; 4] =
["schemaVersion", "operation", "files", "completeness"];
trait ExtensionPolicy: Copy {
type Key;
type Sink;
fn key(name: &str) -> Self::Key;
fn sink() -> Self::Sink;
fn absorb<'de, A>(sink: &mut Self::Sink, key: Self::Key, map: &mut A) -> Result<(), A::Error>
where
A: MapAccess<'de>;
fn finish(sink: Self::Sink) -> BTreeMap<String, Value>;
}
#[derive(Clone, Copy)]
struct Capture;
impl ExtensionPolicy for Capture {
type Key = String;
type Sink = BTreeMap<String, Value>;
fn key(name: &str) -> String {
name.to_owned()
}
fn sink() -> Self::Sink {
BTreeMap::new()
}
fn absorb<'de, A>(sink: &mut Self::Sink, key: String, map: &mut A) -> Result<(), A::Error>
where
A: MapAccess<'de>,
{
sink.insert(key, map.next_value()?);
Ok(())
}
fn finish(sink: Self::Sink) -> BTreeMap<String, Value> {
sink
}
}
#[derive(Clone, Copy)]
struct Discard;
impl ExtensionPolicy for Discard {
type Key = ();
type Sink = ();
fn key(_name: &str) {}
fn sink() -> Self::Sink {}
fn absorb<'de, A>(_sink: &mut (), _key: (), map: &mut A) -> Result<(), A::Error>
where
A: MapAccess<'de>,
{
map.next_value::<IgnoredAny>()?;
Ok(())
}
fn finish((): Self::Sink) -> BTreeMap<String, Value> {
BTreeMap::new()
}
}
enum Field<K> {
Known(usize),
Unknown(K),
}
trait FieldTable {
fn lookup(name: &str) -> Option<usize>;
}
struct TextEditFields;
impl FieldTable for TextEditFields {
fn lookup(name: &str) -> Option<usize> {
Some(match name {
"startLine" => 0,
"startChar" => 1,
"endLine" => 2,
"endChar" => 3,
"before" => 4,
"after" => 5,
"provenance" => 6,
_ => return None,
})
}
}
struct FileEditFields;
impl FieldTable for FileEditFields {
fn lookup(name: &str) -> Option<usize> {
Some(match name {
"path" => 0,
"sha256" => 1,
"edits" => 2,
_ => return None,
})
}
}
struct EditPlanFields;
impl FieldTable for EditPlanFields {
fn lookup(name: &str) -> Option<usize> {
Some(match name {
"schemaVersion" => 0,
"operation" => 1,
"files" => 2,
"completeness" => 3,
_ => return None,
})
}
}
struct FieldSeed<P, T>(PhantomData<(P, T)>);
impl<P, T> Clone for FieldSeed<P, T> {
fn clone(&self) -> Self {
*self
}
}
impl<P, T> Copy for FieldSeed<P, T> {}
impl<P, T> FieldSeed<P, T> {
const fn new() -> Self {
Self(PhantomData)
}
}
impl<'de, P: ExtensionPolicy, T: FieldTable> DeserializeSeed<'de> for FieldSeed<P, T> {
type Value = Field<P::Key>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_identifier(self)
}
}
impl<P: ExtensionPolicy, T: FieldTable> Visitor<'_> for FieldSeed<P, T> {
type Value = Field<P::Key>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("field identifier")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(match T::lookup(value) {
Some(index) => Field::Known(index),
None => Field::Unknown(P::key(value)),
})
}
}
struct VecSeed<S>(S);
impl<'de, S> DeserializeSeed<'de> for VecSeed<S>
where
S: DeserializeSeed<'de> + Copy,
{
type Value = Vec<S::Value>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(self)
}
}
impl<'de, S> Visitor<'de> for VecSeed<S>
where
S: DeserializeSeed<'de> + Copy,
{
type Value = Vec<S::Value>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a sequence")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut items = Vec::with_capacity(cautious_capacity::<S::Value>(seq.size_hint()));
while let Some(item) = seq.next_element_seed(self.0)? {
items.push(item);
}
Ok(items)
}
}
fn cautious_capacity<T>(hint: Option<usize>) -> usize {
const MAX_PREALLOCATED_BYTES: usize = 1024 * 1024;
MAX_PREALLOCATED_BYTES
.checked_div(size_of::<T>())
.map_or(0, |ceiling| hint.unwrap_or(0).min(ceiling))
}
struct TextEditSeed<P>(PhantomData<P>);
impl<P> Clone for TextEditSeed<P> {
fn clone(&self) -> Self {
*self
}
}
impl<P> Copy for TextEditSeed<P> {}
impl<P> TextEditSeed<P> {
const fn new() -> Self {
Self(PhantomData)
}
}
impl<'de, P: ExtensionPolicy> DeserializeSeed<'de> for TextEditSeed<P> {
type Value = TextEdit;
fn deserialize<D>(self, deserializer: D) -> Result<TextEdit, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(self)
}
}
impl<'de, P: ExtensionPolicy> Visitor<'de> for TextEditSeed<P> {
type Value = TextEdit;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct TextEdit")
}
fn visit_map<A>(self, mut map: A) -> Result<TextEdit, A::Error>
where
A: MapAccess<'de>,
{
let mut start_line: Option<u32> = None;
let mut start_char: Option<u32> = None;
let mut end_line: Option<u32> = None;
let mut end_char: Option<u32> = None;
let mut before: Option<String> = None;
let mut after: Option<String> = None;
let mut provenance: Option<Provenance> = None;
let mut sink = P::sink();
while let Some(field) = map.next_key_seed(FieldSeed::<P, TextEditFields>::new())? {
match field {
Field::Known(0) => {
if start_line.is_some() {
return Err(de::Error::duplicate_field("startLine"));
}
start_line = Some(map.next_value()?);
}
Field::Known(1) => {
if start_char.is_some() {
return Err(de::Error::duplicate_field("startChar"));
}
start_char = Some(map.next_value()?);
}
Field::Known(2) => {
if end_line.is_some() {
return Err(de::Error::duplicate_field("endLine"));
}
end_line = Some(map.next_value()?);
}
Field::Known(3) => {
if end_char.is_some() {
return Err(de::Error::duplicate_field("endChar"));
}
end_char = Some(map.next_value()?);
}
Field::Known(4) => {
if before.is_some() {
return Err(de::Error::duplicate_field("before"));
}
before = Some(map.next_value()?);
}
Field::Known(5) => {
if after.is_some() {
return Err(de::Error::duplicate_field("after"));
}
after = Some(map.next_value()?);
}
Field::Known(_) => {
if provenance.is_some() {
return Err(de::Error::duplicate_field("provenance"));
}
provenance = Some(map.next_value()?);
}
Field::Unknown(key) => P::absorb(&mut sink, key, &mut map)?,
}
}
Ok(TextEdit {
start_line: start_line.ok_or_else(|| de::Error::missing_field("startLine"))?,
start_char: start_char.ok_or_else(|| de::Error::missing_field("startChar"))?,
end_line: end_line.ok_or_else(|| de::Error::missing_field("endLine"))?,
end_char: end_char.ok_or_else(|| de::Error::missing_field("endChar"))?,
before: before.ok_or_else(|| de::Error::missing_field("before"))?,
after: after.ok_or_else(|| de::Error::missing_field("after"))?,
provenance: provenance.ok_or_else(|| de::Error::missing_field("provenance"))?,
extensions: P::finish(sink),
})
}
}
impl<'de> Deserialize<'de> for TextEdit {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
TextEditSeed::<Capture>::new().deserialize(deserializer)
}
}
impl Serialize for TextEdit {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry(TEXT_EDIT_FIELDS[0], &self.start_line)?;
map.serialize_entry(TEXT_EDIT_FIELDS[1], &self.start_char)?;
map.serialize_entry(TEXT_EDIT_FIELDS[2], &self.end_line)?;
map.serialize_entry(TEXT_EDIT_FIELDS[3], &self.end_char)?;
map.serialize_entry(TEXT_EDIT_FIELDS[4], &self.before)?;
map.serialize_entry(TEXT_EDIT_FIELDS[5], &self.after)?;
map.serialize_entry(TEXT_EDIT_FIELDS[6], &self.provenance)?;
serialize_extensions(&mut map, &self.extensions)?;
map.end()
}
}
struct FileEditSeed<P>(PhantomData<P>);
impl<P> Clone for FileEditSeed<P> {
fn clone(&self) -> Self {
*self
}
}
impl<P> Copy for FileEditSeed<P> {}
impl<P> FileEditSeed<P> {
const fn new() -> Self {
Self(PhantomData)
}
}
impl<'de, P: ExtensionPolicy> DeserializeSeed<'de> for FileEditSeed<P> {
type Value = FileEdit;
fn deserialize<D>(self, deserializer: D) -> Result<FileEdit, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(self)
}
}
impl<'de, P: ExtensionPolicy> Visitor<'de> for FileEditSeed<P> {
type Value = FileEdit;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct FileEdit")
}
fn visit_map<A>(self, mut map: A) -> Result<FileEdit, A::Error>
where
A: MapAccess<'de>,
{
let mut path: Option<String> = None;
let mut sha256: Option<String> = None;
let mut edits: Option<Vec<TextEdit>> = None;
let mut sink = P::sink();
while let Some(field) = map.next_key_seed(FieldSeed::<P, FileEditFields>::new())? {
match field {
Field::Known(0) => {
if path.is_some() {
return Err(de::Error::duplicate_field("path"));
}
path = Some(map.next_value()?);
}
Field::Known(1) => {
if sha256.is_some() {
return Err(de::Error::duplicate_field("sha256"));
}
sha256 = Some(map.next_value()?);
}
Field::Known(_) => {
if edits.is_some() {
return Err(de::Error::duplicate_field("edits"));
}
edits = Some(map.next_value_seed(VecSeed(TextEditSeed::<P>::new()))?);
}
Field::Unknown(key) => P::absorb(&mut sink, key, &mut map)?,
}
}
Ok(FileEdit {
path: path.ok_or_else(|| de::Error::missing_field("path"))?,
sha256: sha256.ok_or_else(|| de::Error::missing_field("sha256"))?,
edits: edits.ok_or_else(|| de::Error::missing_field("edits"))?,
extensions: P::finish(sink),
})
}
}
impl<'de> Deserialize<'de> for FileEdit {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
FileEditSeed::<Capture>::new().deserialize(deserializer)
}
}
impl Serialize for FileEdit {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry(FILE_EDIT_FIELDS[0], &self.path)?;
map.serialize_entry(FILE_EDIT_FIELDS[1], &self.sha256)?;
map.serialize_entry(FILE_EDIT_FIELDS[2], &self.edits)?;
serialize_extensions(&mut map, &self.extensions)?;
map.end()
}
}
struct EditPlanSeed<P>(PhantomData<P>);
impl<P> Clone for EditPlanSeed<P> {
fn clone(&self) -> Self {
*self
}
}
impl<P> Copy for EditPlanSeed<P> {}
impl<P> EditPlanSeed<P> {
const fn new() -> Self {
Self(PhantomData)
}
}
impl<'de, P: ExtensionPolicy> DeserializeSeed<'de> for EditPlanSeed<P> {
type Value = EditPlan;
fn deserialize<D>(self, deserializer: D) -> Result<EditPlan, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(self)
}
}
impl<'de, P: ExtensionPolicy> Visitor<'de> for EditPlanSeed<P> {
type Value = EditPlan;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct EditPlan")
}
fn visit_map<A>(self, mut map: A) -> Result<EditPlan, A::Error>
where
A: MapAccess<'de>,
{
let mut schema_version: Option<String> = None;
let mut operation: Option<String> = None;
let mut files: Option<Vec<FileEdit>> = None;
let mut completeness: Option<Option<Completeness>> = None;
let mut sink = P::sink();
while let Some(field) = map.next_key_seed(FieldSeed::<P, EditPlanFields>::new())? {
match field {
Field::Known(0) => {
if schema_version.is_some() {
return Err(de::Error::duplicate_field("schemaVersion"));
}
schema_version = Some(map.next_value()?);
}
Field::Known(1) => {
if operation.is_some() {
return Err(de::Error::duplicate_field("operation"));
}
operation = Some(map.next_value()?);
}
Field::Known(2) => {
if files.is_some() {
return Err(de::Error::duplicate_field("files"));
}
files = Some(map.next_value_seed(VecSeed(FileEditSeed::<P>::new()))?);
}
Field::Known(_) => {
if completeness.is_some() {
return Err(de::Error::duplicate_field("completeness"));
}
completeness = Some(map.next_value()?);
}
Field::Unknown(key) => P::absorb(&mut sink, key, &mut map)?,
}
}
Ok(EditPlan {
schema_version: schema_version
.ok_or_else(|| de::Error::missing_field("schemaVersion"))?,
operation: operation.ok_or_else(|| de::Error::missing_field("operation"))?,
files: files.ok_or_else(|| de::Error::missing_field("files"))?,
completeness: completeness.unwrap_or_default(),
extensions: P::finish(sink),
})
}
}
impl<'de> Deserialize<'de> for EditPlan {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
EditPlanSeed::<Capture>::new().deserialize(deserializer)
}
}
impl Serialize for EditPlan {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry(EDIT_PLAN_FIELDS[0], &self.schema_version)?;
map.serialize_entry(EDIT_PLAN_FIELDS[1], &self.operation)?;
map.serialize_entry(EDIT_PLAN_FIELDS[2], &self.files)?;
if self.completeness.is_some() {
map.serialize_entry(EDIT_PLAN_FIELDS[3], &self.completeness)?;
}
serialize_extensions(&mut map, &self.extensions)?;
map.end()
}
}
fn serialize_extensions<M>(
map: &mut M,
extensions: &BTreeMap<String, Value>,
) -> Result<(), M::Error>
where
M: SerializeMap,
{
for (key, value) in extensions {
map.serialize_entry(key, value)?;
}
Ok(())
}
#[derive(Clone, Debug, PartialEq)]
pub struct DeclaredEditPlan(EditPlan);
impl DeclaredEditPlan {
#[must_use]
pub const fn plan(&self) -> &EditPlan {
&self.0
}
#[must_use]
pub fn into_plan(self) -> EditPlan {
self.0
}
}
impl AsRef<EditPlan> for DeclaredEditPlan {
fn as_ref(&self) -> &EditPlan {
&self.0
}
}
impl From<DeclaredEditPlan> for EditPlan {
fn from(declared: DeclaredEditPlan) -> Self {
declared.0
}
}
impl<'de> Deserialize<'de> for DeclaredEditPlan {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
EditPlanSeed::<Discard>::new()
.deserialize(deserializer)
.map(Self)
}
}
#[cfg(test)]
mod tests {
use super::{EDIT_PLAN_FIELDS, FILE_EDIT_FIELDS, TEXT_EDIT_FIELDS};
use crate::validation::FILE_EDIT_RESERVED_EXTENSION_KEYS;
#[test]
fn reserved_extension_keys_track_the_wire_field_names() {
assert_eq!(FILE_EDIT_RESERVED_EXTENSION_KEYS, FILE_EDIT_FIELDS);
assert_eq!(FILE_EDIT_FIELDS.len(), 3);
assert_eq!(TEXT_EDIT_FIELDS.len(), 7);
assert_eq!(EDIT_PLAN_FIELDS.len(), 4);
}
}