Skip to main content

semtree_core/
chunk.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::path::PathBuf;
4
5use crate::{Language, Span};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum ChunkKind {
10    Function,
11    Method,
12    Struct,
13    Enum,
14    Trait,
15    Impl,
16    Module,
17    Class,
18    File,
19}
20
21impl ChunkKind {
22    /// Parse a user-facing kind name, as accepted by a `--kind` filter.
23    /// Case-insensitive; returns `None` for anything unrecognized.
24    pub fn from_name(name: &str) -> Option<Self> {
25        Some(match name.to_lowercase().as_str() {
26            "function" | "fn" | "func" => Self::Function,
27            "method" => Self::Method,
28            "struct" => Self::Struct,
29            "enum" => Self::Enum,
30            "trait" => Self::Trait,
31            "impl" => Self::Impl,
32            "module" | "mod" => Self::Module,
33            "class" => Self::Class,
34            "file" => Self::File,
35            _ => return None,
36        })
37    }
38
39    /// Every kind the parser can produce, in a stable order.
40    pub const ALL: &'static [Self] = &[
41        Self::Function,
42        Self::Method,
43        Self::Struct,
44        Self::Enum,
45        Self::Trait,
46        Self::Impl,
47        Self::Module,
48        Self::Class,
49        Self::File,
50    ];
51}
52
53impl fmt::Display for ChunkKind {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        let s = match self {
56            Self::Function => "function",
57            Self::Method => "method",
58            Self::Struct => "struct",
59            Self::Enum => "enum",
60            Self::Trait => "trait",
61            Self::Impl => "impl",
62            Self::Module => "module",
63            Self::Class => "class",
64            Self::File => "file",
65        };
66        f.write_str(s)
67    }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct Chunk {
72    /// Unique identifier (hash of path + span)
73    pub id: String,
74    /// Source file
75    pub path: PathBuf,
76    /// Programming language
77    pub language: Language,
78    /// Kind of code construct
79    pub kind: ChunkKind,
80    /// Name of the construct (e.g. function name)
81    pub name: Option<String>,
82    /// Raw source text
83    pub content: String,
84    /// Location in source file
85    pub span: Span,
86    /// Docstring / leading comment if any
87    pub doc: Option<String>,
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn every_kind_parses_back_from_its_own_name() {
96        for kind in ChunkKind::ALL {
97            assert_eq!(
98                ChunkKind::from_name(&kind.to_string()),
99                Some(*kind),
100                "{kind} does not round-trip through from_name"
101            );
102        }
103    }
104
105    #[test]
106    fn kind_names_are_case_insensitive_and_accept_short_forms() {
107        assert_eq!(ChunkKind::from_name("FN"), Some(ChunkKind::Function));
108        assert_eq!(ChunkKind::from_name("Mod"), Some(ChunkKind::Module));
109        assert_eq!(ChunkKind::from_name("nope"), None);
110    }
111}