Skip to main content

slack_web_api/
form.rs

1// リクエストを `application/x-www-form-urlencoded` に直列化する。
2//
3// Slack の全メソッドがこの形式を受け付ける(JSON 本文は一部のメソッドが受け付けない)。
4// 値の扱いは公式 SDK と同じ:
5// - 文字列・数値・真偽値はそのまま
6// - 文字列などの配列はカンマ区切り(`U1,U2`)
7// - オブジェクト・オブジェクトの配列(blocks・attachments 等)は JSON 文字列
8// - `None` は送らない
9//
10// `serde_json::Value` の木を経由せず、フィールドごとに直接書き出す。
11
12use serde::ser::{self, Impossible, Serialize};
13use std::fmt;
14
15// 構造体(またはマップ)をフォーム本文にする
16pub(crate) fn to_form<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
17    let mut out = form_urlencoded::Serializer::new(String::new());
18    value.serialize(TopSerializer { out: &mut out })?;
19    Ok(out.finish())
20}
21
22/// `serialize_with` helper that sends a list as a JSON array string instead of a comma-separated list.
23/// Used by arguments whose documented example looks like `["C1","C2"]`.
24///
25/// # Errors
26///
27/// Fails when `value` cannot be serialized to JSON (for example, a map with non-string keys).
28pub fn as_json<T: Serialize, S: ser::Serializer>(
29    value: &T,
30    serializer: S,
31) -> Result<S::Ok, S::Error> {
32    let json = serde_json::to_string(value).map_err(ser::Error::custom)?;
33    serializer.serialize_str(&json)
34}
35
36#[derive(Debug)]
37pub(crate) enum Error {
38    // フィールドの値がスカラーで表せない(JSON にフォールバックする合図)
39    NotScalar,
40    Custom(String),
41}
42
43impl fmt::Display for Error {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        match self {
46            Error::NotScalar => f.write_str("value is not a scalar"),
47            Error::Custom(msg) => f.write_str(msg),
48        }
49    }
50}
51
52impl std::error::Error for Error {}
53
54impl ser::Error for Error {
55    fn custom<T: fmt::Display>(msg: T) -> Self {
56        Error::Custom(msg.to_string())
57    }
58}
59
60type Out = form_urlencoded::Serializer<'static, String>;
61
62// 1フィールド分の値を書き出す。スカラー・スカラー配列ならそのまま、それ以外は JSON 文字列
63fn write_field<T: Serialize + ?Sized>(out: &mut Out, key: &str, value: &T) -> Result<(), Error> {
64    match value.serialize(ScalarSerializer) {
65        Ok(Some(text)) => {
66            out.append_pair(key, &text);
67            Ok(())
68        }
69        Ok(None) => Ok(()),
70        Err(Error::NotScalar) => {
71            let json = serde_json::to_string(value).map_err(ser::Error::custom)?;
72            out.append_pair(key, &json);
73            Ok(())
74        }
75        Err(e) => Err(e),
76    }
77}
78
79struct TopSerializer<'a> {
80    out: &'a mut Out,
81}
82
83macro_rules! top_level_only {
84    ($($name:ident($($arg:ty),*) -> $ret:ty;)*) => {
85        $(fn $name(self, $(_: $arg),*) -> Result<$ret, Error> {
86            Err(Error::Custom("form body must be a struct or map".into()))
87        })*
88    };
89}
90
91impl<'a> ser::Serializer for TopSerializer<'a> {
92    type Ok = ();
93    type Error = Error;
94    type SerializeSeq = Impossible<(), Error>;
95    type SerializeTuple = Impossible<(), Error>;
96    type SerializeTupleStruct = Impossible<(), Error>;
97    type SerializeTupleVariant = Impossible<(), Error>;
98    type SerializeMap = TopMap<'a>;
99    type SerializeStruct = Self;
100    type SerializeStructVariant = Impossible<(), Error>;
101
102    fn serialize_struct(self, _: &'static str, _: usize) -> Result<Self, Error> {
103        Ok(self)
104    }
105    fn serialize_map(self, _: Option<usize>) -> Result<TopMap<'a>, Error> {
106        Ok(TopMap {
107            out: self.out,
108            key: None,
109        })
110    }
111    fn serialize_unit_struct(self, _: &'static str) -> Result<(), Error> {
112        Ok(())
113    }
114    fn serialize_unit(self) -> Result<(), Error> {
115        Ok(())
116    }
117    fn serialize_newtype_struct<T: Serialize + ?Sized>(
118        self,
119        _: &'static str,
120        value: &T,
121    ) -> Result<(), Error> {
122        value.serialize(self)
123    }
124
125    top_level_only! {
126        serialize_bool(bool) -> ();
127        serialize_i8(i8) -> ();
128        serialize_i16(i16) -> ();
129        serialize_i32(i32) -> ();
130        serialize_i64(i64) -> ();
131        serialize_u8(u8) -> ();
132        serialize_u16(u16) -> ();
133        serialize_u32(u32) -> ();
134        serialize_u64(u64) -> ();
135        serialize_f32(f32) -> ();
136        serialize_f64(f64) -> ();
137        serialize_char(char) -> ();
138        serialize_str(&str) -> ();
139        serialize_bytes(&[u8]) -> ();
140        serialize_none() -> ();
141        serialize_unit_variant(&'static str, u32, &'static str) -> ();
142        serialize_seq(Option<usize>) -> Self::SerializeSeq;
143        serialize_tuple(usize) -> Self::SerializeTuple;
144        serialize_tuple_struct(&'static str, usize) -> Self::SerializeTupleStruct;
145        serialize_tuple_variant(&'static str, u32, &'static str, usize) -> Self::SerializeTupleVariant;
146        serialize_struct_variant(&'static str, u32, &'static str, usize) -> Self::SerializeStructVariant;
147    }
148    fn serialize_some<T: Serialize + ?Sized>(self, _: &T) -> Result<(), Error> {
149        Err(Error::Custom("form body must be a struct or map".into()))
150    }
151    fn serialize_newtype_variant<T: Serialize + ?Sized>(
152        self,
153        _: &'static str,
154        _: u32,
155        _: &'static str,
156        _: &T,
157    ) -> Result<(), Error> {
158        Err(Error::Custom("form body must be a struct or map".into()))
159    }
160}
161
162impl ser::SerializeStruct for TopSerializer<'_> {
163    type Ok = ();
164    type Error = Error;
165    fn serialize_field<T: Serialize + ?Sized>(
166        &mut self,
167        key: &'static str,
168        value: &T,
169    ) -> Result<(), Error> {
170        write_field(self.out, key, value)
171    }
172    fn end(self) -> Result<(), Error> {
173        Ok(())
174    }
175}
176
177struct TopMap<'a> {
178    out: &'a mut Out,
179    key: Option<String>,
180}
181
182impl ser::SerializeMap for TopMap<'_> {
183    type Ok = ();
184    type Error = Error;
185    fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), Error> {
186        match key.serialize(ScalarSerializer)? {
187            Some(k) => {
188                self.key = Some(k);
189                Ok(())
190            }
191            None => Err(Error::Custom("map key must not be null".into())),
192        }
193    }
194    fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
195        let key = self
196            .key
197            .take()
198            .ok_or_else(|| Error::Custom("map value without key".into()))?;
199        write_field(self.out, &key, value)
200    }
201    fn end(self) -> Result<(), Error> {
202        Ok(())
203    }
204}
205
206// スカラーを文字列にする。None/unit は Ok(None)(送らない)。構造を持つ値は NotScalar
207struct ScalarSerializer;
208
209macro_rules! scalar_display {
210    ($($name:ident($ty:ty);)*) => {
211        $(fn $name(self, v: $ty) -> Result<Option<String>, Error> {
212            Ok(Some(v.to_string()))
213        })*
214    };
215}
216
217impl ser::Serializer for ScalarSerializer {
218    type Ok = Option<String>;
219    type Error = Error;
220    type SerializeSeq = JoinSeq;
221    type SerializeTuple = Impossible<Option<String>, Error>;
222    type SerializeTupleStruct = Impossible<Option<String>, Error>;
223    type SerializeTupleVariant = Impossible<Option<String>, Error>;
224    type SerializeMap = Impossible<Option<String>, Error>;
225    type SerializeStruct = Impossible<Option<String>, Error>;
226    type SerializeStructVariant = Impossible<Option<String>, Error>;
227
228    scalar_display! {
229        serialize_bool(bool);
230        serialize_i8(i8);
231        serialize_i16(i16);
232        serialize_i32(i32);
233        serialize_i64(i64);
234        serialize_u8(u8);
235        serialize_u16(u16);
236        serialize_u32(u32);
237        serialize_u64(u64);
238        serialize_f32(f32);
239        serialize_f64(f64);
240        serialize_char(char);
241    }
242    fn serialize_str(self, v: &str) -> Result<Option<String>, Error> {
243        Ok(Some(v.to_owned()))
244    }
245    fn serialize_bytes(self, _: &[u8]) -> Result<Option<String>, Error> {
246        Err(Error::NotScalar)
247    }
248    fn serialize_none(self) -> Result<Option<String>, Error> {
249        Ok(None)
250    }
251    fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<Option<String>, Error> {
252        value.serialize(self)
253    }
254    fn serialize_unit(self) -> Result<Option<String>, Error> {
255        Ok(None)
256    }
257    fn serialize_unit_struct(self, _: &'static str) -> Result<Option<String>, Error> {
258        Ok(None)
259    }
260    fn serialize_unit_variant(
261        self,
262        _: &'static str,
263        _: u32,
264        variant: &'static str,
265    ) -> Result<Option<String>, Error> {
266        Ok(Some(variant.to_owned()))
267    }
268    fn serialize_newtype_struct<T: Serialize + ?Sized>(
269        self,
270        _: &'static str,
271        value: &T,
272    ) -> Result<Option<String>, Error> {
273        value.serialize(self)
274    }
275    fn serialize_newtype_variant<T: Serialize + ?Sized>(
276        self,
277        _: &'static str,
278        _: u32,
279        _: &'static str,
280        _: &T,
281    ) -> Result<Option<String>, Error> {
282        Err(Error::NotScalar)
283    }
284    fn serialize_seq(self, _: Option<usize>) -> Result<JoinSeq, Error> {
285        Ok(JoinSeq {
286            joined: String::new(),
287            first: true,
288        })
289    }
290    fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Error> {
291        Err(Error::NotScalar)
292    }
293    fn serialize_tuple_struct(
294        self,
295        _: &'static str,
296        _: usize,
297    ) -> Result<Self::SerializeTupleStruct, Error> {
298        Err(Error::NotScalar)
299    }
300    fn serialize_tuple_variant(
301        self,
302        _: &'static str,
303        _: u32,
304        _: &'static str,
305        _: usize,
306    ) -> Result<Self::SerializeTupleVariant, Error> {
307        Err(Error::NotScalar)
308    }
309    fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Error> {
310        Err(Error::NotScalar)
311    }
312    fn serialize_struct(self, _: &'static str, _: usize) -> Result<Self::SerializeStruct, Error> {
313        Err(Error::NotScalar)
314    }
315    fn serialize_struct_variant(
316        self,
317        _: &'static str,
318        _: u32,
319        _: &'static str,
320        _: usize,
321    ) -> Result<Self::SerializeStructVariant, Error> {
322        Err(Error::NotScalar)
323    }
324}
325
326// スカラーの配列をカンマ区切りにする。要素がスカラーでなければ NotScalar(JSON にフォールバック)
327struct JoinSeq {
328    joined: String,
329    first: bool,
330}
331
332impl ser::SerializeSeq for JoinSeq {
333    type Ok = Option<String>;
334    type Error = Error;
335    fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
336        // 配列の中の null はカンマ区切りで表せない
337        let text = value.serialize(ScalarSerializer)?.ok_or(Error::NotScalar)?;
338        if !self.first {
339            self.joined.push(',');
340        }
341        self.first = false;
342        self.joined.push_str(&text);
343        Ok(())
344    }
345    fn end(self) -> Result<Option<String>, Error> {
346        Ok(Some(self.joined))
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353    use serde::Serialize;
354    use serde_json::json;
355    use std::collections::BTreeMap;
356
357    #[derive(Serialize)]
358    struct Req {
359        channel: String,
360        limit: Option<i64>,
361        inclusive: Option<bool>,
362        ratio: f64,
363        users: Vec<String>,
364        blocks: Vec<serde_json::Value>,
365        skipped: Option<String>,
366        #[serde(serialize_with = "as_json")]
367        ids: Vec<String>,
368        letter: char,
369        kind: Kind,
370        unit: (),
371        wrapped: Wrapped,
372    }
373
374    #[derive(Serialize)]
375    #[serde(rename_all = "snake_case")]
376    enum Kind {
377        PublicChannel,
378    }
379
380    #[derive(Serialize)]
381    struct Wrapped(u8);
382
383    fn sample() -> Req {
384        Req {
385            channel: "C1 #x&y".into(),
386            limit: Some(200),
387            inclusive: Some(true),
388            ratio: 1.5,
389            users: vec!["U1".into(), "U2".into()],
390            blocks: vec![json!({"type": "divider"})],
391            skipped: None,
392            ids: vec!["A".into(), "B".into()],
393            letter: 'z',
394            kind: Kind::PublicChannel,
395            unit: (),
396            wrapped: Wrapped(7),
397        }
398    }
399
400    fn decode(body: &str) -> Vec<(String, String)> {
401        form_urlencoded::parse(body.as_bytes())
402            .into_owned()
403            .collect()
404    }
405
406    #[test]
407    fn struct_fields_follow_slack_conventions() {
408        let pairs = decode(&to_form(&sample()).unwrap());
409        assert_eq!(
410            pairs,
411            vec![
412                ("channel".into(), "C1 #x&y".into()),
413                ("limit".into(), "200".into()),
414                ("inclusive".into(), "true".into()),
415                ("ratio".into(), "1.5".into()),
416                ("users".into(), "U1,U2".into()),
417                ("blocks".into(), r#"[{"type":"divider"}]"#.into()),
418                ("ids".into(), r#"["A","B"]"#.into()),
419                ("letter".into(), "z".into()),
420                ("kind".into(), "public_channel".into()),
421                ("wrapped".into(), "7".into()),
422            ]
423        );
424    }
425
426    #[test]
427    fn integer_widths_are_written_as_numbers() {
428        #[derive(Serialize)]
429        struct Ints {
430            a: i8,
431            b: i16,
432            c: i32,
433            d: u16,
434            e: u32,
435            f: u64,
436            g: f32,
437        }
438        let body = to_form(&Ints {
439            a: -1,
440            b: 2,
441            c: 3,
442            d: 4,
443            e: 5,
444            f: 6,
445            g: 0.5,
446        })
447        .unwrap();
448        assert_eq!(body, "a=-1&b=2&c=3&d=4&e=5&f=6&g=0.5");
449    }
450
451    #[test]
452    fn map_and_value_bodies() {
453        let mut map = BTreeMap::new();
454        map.insert("channel", json!("C1"));
455        map.insert("nested", json!({"a": 1}));
456        map.insert("null", serde_json::Value::Null);
457        map.insert("list", json!(["x", 1, true]));
458        map.insert("mixed", json!(["x", null]));
459        assert_eq!(
460            decode(&to_form(&map).unwrap()),
461            vec![
462                ("channel".into(), "C1".into()),
463                ("list".into(), "x,1,true".into()),
464                ("mixed".into(), r#"["x",null]"#.into()),
465                ("nested".into(), r#"{"a":1}"#.into()),
466            ]
467        );
468        assert_eq!(to_form(&json!({"a": "b"})).unwrap(), "a=b");
469    }
470
471    #[test]
472    fn non_scalar_shapes_fall_back_to_json() {
473        #[derive(Serialize)]
474        enum Shape {
475            New(u8),
476            Tuple(u8, u8),
477            Struct { x: u8 },
478        }
479        #[derive(Serialize)]
480        struct TupleStruct(u8, u8);
481        #[derive(Serialize)]
482        struct Inner {
483            x: u8,
484        }
485        #[derive(Serialize)]
486        struct Req<'a> {
487            new: Shape,
488            tuple_variant: Shape,
489            struct_variant: Shape,
490            tuple: (u8, u8),
491            tuple_struct: TupleStruct,
492            inner: Inner,
493            #[serde(with = "serde_bytes_like")]
494            bytes: &'a [u8],
495        }
496        mod serde_bytes_like {
497            pub fn serialize<S: serde::Serializer>(v: &&[u8], s: S) -> Result<S::Ok, S::Error> {
498                s.serialize_bytes(v)
499            }
500        }
501        let body = to_form(&Req {
502            new: Shape::New(1),
503            tuple_variant: Shape::Tuple(1, 2),
504            struct_variant: Shape::Struct { x: 3 },
505            tuple: (4, 5),
506            tuple_struct: TupleStruct(6, 7),
507            inner: Inner { x: 8 },
508            bytes: b"hi",
509        })
510        .unwrap();
511        assert_eq!(
512            decode(&body),
513            vec![
514                ("new".into(), r#"{"New":1}"#.into()),
515                ("tuple_variant".into(), r#"{"Tuple":[1,2]}"#.into()),
516                ("struct_variant".into(), r#"{"Struct":{"x":3}}"#.into()),
517                ("tuple".into(), "[4,5]".into()),
518                ("tuple_struct".into(), "[6,7]".into()),
519                ("inner".into(), r#"{"x":8}"#.into()),
520                ("bytes".into(), "[104,105]".into()),
521            ]
522        );
523    }
524
525    #[test]
526    fn unit_struct_fields_are_skipped() {
527        #[derive(Serialize)]
528        struct Marker;
529        #[derive(Serialize)]
530        struct Req {
531            a: Marker,
532            b: u8,
533        }
534        assert_eq!(to_form(&Req { a: Marker, b: 1 }).unwrap(), "b=1");
535    }
536
537    #[test]
538    fn unit_and_newtype_top_levels() {
539        #[derive(Serialize)]
540        struct Empty;
541        #[derive(Serialize)]
542        struct Newtype(BTreeMap<&'static str, &'static str>);
543        assert_eq!(to_form(&Empty).unwrap(), "");
544        assert_eq!(to_form(&()).unwrap(), "");
545        assert_eq!(
546            to_form(&Newtype(BTreeMap::from([("a", "b")]))).unwrap(),
547            "a=b"
548        );
549    }
550
551    #[test]
552    fn scalar_top_levels_are_rejected() {
553        #[derive(Serialize)]
554        enum E {
555            Unit,
556            New(u8),
557            Tuple(u8),
558            Struct { x: u8 },
559        }
560        #[derive(Serialize)]
561        struct TupleStruct(u8, u8);
562        let rejected: Vec<Result<String, Error>> = vec![
563            to_form(&true),
564            to_form(&1i8),
565            to_form(&1i16),
566            to_form(&1i32),
567            to_form(&1i64),
568            to_form(&1u8),
569            to_form(&1u16),
570            to_form(&1u32),
571            to_form(&1u64),
572            to_form(&1f32),
573            to_form(&1f64),
574            to_form(&'c'),
575            to_form("s"),
576            to_form(&Some(1)),
577            to_form(&E::Unit),
578            to_form(&E::New(1)),
579            to_form(&E::Struct { x: 1 }),
580            to_form(&vec![1]),
581            to_form(&(1, 2)),
582            to_form(&TupleStruct(1, 2)),
583            to_form(&E::Tuple(1)),
584            to_form(&Option::<u8>::None),
585        ];
586        for r in rejected {
587            let err = r.unwrap_err();
588            assert_eq!(err.to_string(), "form body must be a struct or map");
589        }
590        assert!(to_form(serde_bytes_ref(b"x")).is_err());
591    }
592
593    struct BytesRef<'a>(&'a [u8]);
594    impl Serialize for BytesRef<'_> {
595        fn serialize<S: ser::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
596            s.serialize_bytes(self.0)
597        }
598    }
599    fn serde_bytes_ref(b: &[u8]) -> &BytesRef<'_> {
600        Box::leak(Box::new(BytesRef(b)))
601    }
602
603    #[test]
604    fn map_key_errors() {
605        let mut null_key = BTreeMap::new();
606        null_key.insert(Option::<&str>::None, 1);
607        assert_eq!(
608            to_form(&null_key).unwrap_err().to_string(),
609            "map key must not be null"
610        );
611
612        let mut struct_key = BTreeMap::new();
613        struct_key.insert(vec![("a", 1)], 1);
614        // スカラーでないキー(タプルの配列)は NotScalar のまま返る
615        assert_eq!(
616            to_form(&struct_key).unwrap_err().to_string(),
617            "value is not a scalar"
618        );
619    }
620
621    #[test]
622    fn value_without_key_is_an_error() {
623        use ser::SerializeMap;
624        let mut out = form_urlencoded::Serializer::new(String::new());
625        let mut map = TopMap {
626            out: &mut out,
627            key: None,
628        };
629        assert_eq!(
630            map.serialize_value(&1).unwrap_err().to_string(),
631            "map value without key"
632        );
633    }
634
635    #[test]
636    fn custom_errors_propagate() {
637        struct Fails;
638        impl Serialize for Fails {
639            fn serialize<S: ser::Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
640                Err(ser::Error::custom("boom"))
641            }
642        }
643        #[derive(Serialize)]
644        struct Req {
645            f: Fails,
646        }
647        assert_eq!(to_form(&Req { f: Fails }).unwrap_err().to_string(), "boom");
648
649        // JSON へのフォールバックでも失敗するもの(キーが文字列でないマップ)
650        #[derive(Serialize)]
651        struct Bad {
652            m: BTreeMap<Vec<u8>, u8>,
653        }
654        let err = to_form(&Bad {
655            m: BTreeMap::from([(vec![1], 1)]),
656        })
657        .unwrap_err();
658        assert!(matches!(err, Error::Custom(_)));
659
660        #[derive(Serialize)]
661        struct BadJson {
662            #[serde(serialize_with = "as_json")]
663            m: BTreeMap<Vec<u8>, u8>,
664        }
665        assert!(to_form(&BadJson {
666            m: BTreeMap::from([(vec![1], 1)])
667        })
668        .is_err());
669    }
670}