Макет экземпляра внутри реализации serde - PullRequest
0 голосов
/ 05 января 2019

Я пытаюсь реализовать пользовательскую функцию / метод десериализации, которая использует некоторые внешние функции. Функция создает экземпляр и использует его методы. Он работает нормально, но я не могу понять, как смоделировать сервис в тестах.

Более общий вопрос: как предоставить состояние функции / методу десериализации?

Код ниже иллюстрирует, что я имею в виду.

MagickBook - это внешний сервис, который содержит состояние и содержит некоторую необходимую логику в методе MagickBook::find.

Scroll - десериализуемая структура данных, которая должна быть десериализована с использованием логики MagicBook.

Мне бы хотелось иметь возможность предоставить конкретный экземпляр MagicBook извне, в момент десериализации. Например в тестах.

Rust Playground

use serde::de::{Deserialize, Deserializer}; // 1.0.82
use serde_derive::Deserialize; // 1.0.82
use serde_json; // 1.0.33

struct MagickBook;

// Some service which I want to mock in the test
impl MagickBook {
    fn new() -> Self {
        Self {}
    }

    fn find(&self, _kind: &str) -> isize {
        let effect = 42;
        // Here we do some logic depending on input parameter
        // -- snip --
        return effect;
    }
}

#[derive(Deserialize, PartialEq, Debug)]
struct Scroll {
    #[serde(rename = "kind")]
    #[serde(deserialize_with = "deserialize_effect")]
    effect: isize,
}

fn deserialize_effect<'de, D>(deserializer: D) -> Result<isize, D::Error>
where
    D: Deserializer<'de>,
{
    let book = MagickBook::new();
    Ok(book.find(&String::deserialize(deserializer)?))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn main() {
        let scroll: Scroll = serde_json::from_str("{\"kind\":\"wind\"}").unwrap();
        assert_eq!(scroll, Scroll { effect: 42 });
    }
}

1 Ответ

0 голосов
/ 05 января 2019

Я бы порекомендовал доступ к макету через внутренне изменяемый локальный экземпляр потока.


use serde::{Deserialize, Deserializer};
use std::cell::RefCell;

struct MagickBook {
    value: isize,
}

thread_local! {
    static MAGICK_BOOK: RefCell<MagickBook> = RefCell::new(MagickBook::new());
}

impl MagickBook {
    fn new() -> Self {
        MagickBook { value: 0 }
    }

    fn find(&self, _kind: &str) -> isize {
        let effect = self.value;
        // -- snip --
        effect
    }
}

#[derive(Deserialize, PartialEq, Debug)]
struct Scroll {
    #[serde(rename = "kind", deserialize_with = "deserialize_effect")]
    effect: isize,
}

fn deserialize_effect<'de, D>(deserializer: D) -> Result<isize, D::Error>
where
    D: Deserializer<'de>,
{
    let kind = String::deserialize(deserializer)?;
    Ok(MAGICK_BOOK.with(|book| book.borrow().find(&kind)))
}

#[test]
fn test_deserialize() {
    MAGICK_BOOK.with(|book| book.borrow_mut().value = 42);
    let scroll: Scroll = serde_json::from_str(r#"{"kind":"wind"}"#).unwrap();
    assert_eq!(scroll, Scroll { effect: 42 });
}
...