Skip to content

Commit f2d6614

Browse files
committed
manual serialization
1 parent 518e10a commit f2d6614

File tree

3 files changed

+114
-7
lines changed

3 files changed

+114
-7
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ repository = "https://github.com/rust-lang/rustdoc-types"
1111
serde = {version="1.0.186"}
1212
serde_derive = {version="1.0.186"}
1313
rustc-hash = {version="2", optional=true}
14+
serde_json = "1.0"
1415

1516
[features]
1617
default = []

src/lib.rs

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::path::PathBuf;
2222
#[cfg(feature = "rustc-hash")]
2323
use rustc_hash::FxHashMap as HashMap;
2424
use serde_derive::{Deserialize, Serialize};
25-
25+
use serde::de::Error;
2626

2727
/// The version of JSON output that this crate represents.
2828
///
@@ -201,7 +201,7 @@ pub struct Item {
201201
pub inner: ItemEnum,
202202
}
203203

204-
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
204+
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
205205
#[serde(rename_all = "snake_case")]
206206
/// An attribute, e.g. `#[repr(C)]`
207207
///
@@ -242,11 +242,108 @@ pub enum Attribute {
242242
/// 1. A HIR debug printing, like `"#[attr = Optimize(Speed)]"`
243243
/// 2. The attribute as it appears in source form, like
244244
/// `"#[optimize(speed)]"`.
245-
// XXX: This variant must be last in the enum because it is untagged.
246-
#[serde(untagged)]
247245
Other(String),
248246
}
249247

248+
impl<'de> serde::Deserialize<'de> for Attribute {
249+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
250+
where
251+
D: serde::Deserializer<'de>,
252+
{
253+
let s = String::deserialize(deserializer)?;
254+
255+
// Strip #[ and ] if present
256+
let cleaned = s.trim_start_matches("#[")
257+
.trim_end_matches("]")
258+
.trim();
259+
260+
// Now match on the cleaned string
261+
Ok(match cleaned {
262+
"non_exhaustive" => Attribute::NonExhaustive,
263+
s if s.starts_with("must_use") => {
264+
// Parse reason if present
265+
let reason = if s.len() > "must_use".len() {
266+
Some(s["must_use".len()..].trim_matches(|c| c == '(' || c == ')' || c == '"').to_string())
267+
} else {
268+
None
269+
};
270+
Attribute::MustUse { reason }
271+
},
272+
s if s.starts_with("export_name") => {
273+
let name = s["export_name".len()..].trim_matches(|c| c == '=' || c == ' ' || c == '"').to_string();
274+
Attribute::ExportName(name)
275+
},
276+
277+
s if s.starts_with("export_name") => {
278+
let name = s["export_name".len()..].trim_matches(|c| c == '=' || c == ' ' || c == '"').to_string();
279+
Attribute::ExportName(name)
280+
},
281+
282+
s if s.starts_with("link_section") => {
283+
let section = s["link_section".len()..].trim_matches(|c| c == '=' || c == ' ' || c == '"').to_string();
284+
Attribute::LinkSection(section)
285+
},
286+
287+
"automatically_derived" => Attribute::AutomaticallyDerived,
288+
289+
s if s.starts_with("repr") => {
290+
let repr_str = s["repr".len()..].trim_matches(|c| c == '(' || c == ')').trim();
291+
let parts: Vec<&str> = repr_str.split(',').map(str::trim).collect();
292+
293+
let mut repr = AttributeRepr {
294+
kind: ReprKind::C, // Will be overwritten
295+
align: None,
296+
packed: None,
297+
int: None,
298+
};
299+
300+
for part in parts {
301+
if part.starts_with("align(") {
302+
let align_str = part.trim_start_matches("align(").trim_end_matches(')');
303+
repr.align = Some(align_str.parse().map_err(D::Error::custom)?);
304+
} else if part.starts_with("packed(") {
305+
let packed_str = part.trim_start_matches("packed(").trim_end_matches(')');
306+
repr.packed = Some(packed_str.parse().map_err(D::Error::custom)?);
307+
} else if part == "C" {
308+
repr.kind = ReprKind::C;
309+
} else if part == "transparent" {
310+
repr.kind = ReprKind::Transparent;
311+
} else if ["i8", "i16", "i32", "i64", "i128", "isize",
312+
"u8", "u16", "u32", "u64", "u128", "usize"].contains(&part) {
313+
repr.int = Some(part.to_string());
314+
}
315+
// Add other ReprKind variants as needed
316+
}
317+
318+
Attribute::Repr(repr)
319+
},
320+
321+
"no_mangle" => Attribute::NoMangle,
322+
323+
s if s.starts_with("target_feature") => {
324+
let features_str = s["target_feature".len()..].trim_matches(|c| c == '(' || c == ')').trim();
325+
let enable: Vec<String> = features_str
326+
.split(',')
327+
.filter_map(|feature| {
328+
let feature = feature.trim();
329+
if feature.starts_with("enable = ") {
330+
Some(feature["enable = ".len()..].trim_matches('"').to_string())
331+
} else {
332+
None
333+
}
334+
})
335+
.collect();
336+
Attribute::TargetFeature { enable }
337+
},
338+
339+
340+
// Default case
341+
_ => Attribute::Other(s.to_string()),
342+
})
343+
}
344+
}
345+
346+
250347
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
251348
/// The contents of a `#[repr(...)]` attribute.
252349
///

src/tests.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ use super::*;
33
#[test]
44
fn test_struct_info_roundtrip() {
55
let s = ItemEnum::Struct(Struct {
6-
generics: Generics { params: vec![], where_predicates: vec![] },
7-
kind: StructKind::Plain { fields: vec![], has_stripped_fields: false },
6+
generics: Generics {
7+
params: vec![],
8+
where_predicates: vec![],
9+
},
10+
kind: StructKind::Plain {
11+
fields: vec![],
12+
has_stripped_fields: false,
13+
},
814
impls: vec![],
915
});
1016

@@ -22,7 +28,10 @@ fn test_struct_info_roundtrip() {
2228
#[test]
2329
fn test_union_info_roundtrip() {
2430
let u = ItemEnum::Union(Union {
25-
generics: Generics { params: vec![], where_predicates: vec![] },
31+
generics: Generics {
32+
params: vec![],
33+
where_predicates: vec![],
34+
},
2635
has_stripped_fields: false,
2736
fields: vec![],
2837
impls: vec![],

0 commit comments

Comments
 (0)