makefile_lossless/ast/
archive.rs

1use crate::lossless::{ArchiveMember, ArchiveMembers};
2use crate::SyntaxKind::*;
3use rowan::ast::AstNode;
4
5impl ArchiveMembers {
6    /// Get the archive name (e.g., "libfoo.a" from "libfoo.a(bar.o)")
7    pub fn archive_name(&self) -> Option<String> {
8        // Get the first identifier before the opening parenthesis
9        for element in self.syntax().children_with_tokens() {
10            if let Some(token) = element.as_token() {
11                if token.kind() == IDENTIFIER {
12                    return Some(token.text().to_string());
13                } else if token.kind() == LPAREN {
14                    // Reached the opening parenthesis without finding an identifier
15                    break;
16                }
17            }
18        }
19        None
20    }
21
22    /// Get all member nodes
23    pub fn members(&self) -> impl Iterator<Item = ArchiveMember> + '_ {
24        self.syntax().children().filter_map(ArchiveMember::cast)
25    }
26
27    /// Get all member names as strings
28    pub fn member_names(&self) -> Vec<String> {
29        self.members().map(|m| m.text()).collect()
30    }
31}
32
33impl ArchiveMember {
34    /// Get the text of this archive member
35    pub fn text(&self) -> String {
36        self.syntax().text().to_string().trim().to_string()
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use crate::lossless::parse;
44    use crate::SyntaxKind::ARCHIVE_MEMBERS;
45
46    #[test]
47    fn test_archive_member_parsing() {
48        // Test basic archive member syntax
49        let input = "libfoo.a(bar.o): bar.c\n\tgcc -c bar.c -o bar.o\n\tar r libfoo.a bar.o\n";
50        let parsed = parse(input, None);
51        assert!(
52            parsed.errors.is_empty(),
53            "Should parse archive member without errors"
54        );
55
56        let makefile = parsed.root();
57        let rules: Vec<_> = makefile.rules().collect();
58        assert_eq!(rules.len(), 1);
59
60        // Check that the target is recognized as an archive member
61        let target_text = rules[0].targets().next().unwrap();
62        assert_eq!(target_text, "libfoo.a(bar.o)");
63    }
64
65    #[test]
66    fn test_archive_member_multiple_members() {
67        // Test archive with multiple members
68        let input = "libfoo.a(bar.o baz.o): bar.c baz.c\n\tgcc -c bar.c baz.c\n\tar r libfoo.a bar.o baz.o\n";
69        let parsed = parse(input, None);
70        assert!(
71            parsed.errors.is_empty(),
72            "Should parse multiple archive members"
73        );
74
75        let makefile = parsed.root();
76        let rules: Vec<_> = makefile.rules().collect();
77        assert_eq!(rules.len(), 1);
78    }
79
80    #[test]
81    fn test_archive_member_in_dependencies() {
82        // Test archive members in dependencies
83        let input =
84            "program: main.o libfoo.a(bar.o) libfoo.a(baz.o)\n\tgcc -o program main.o libfoo.a\n";
85        let parsed = parse(input, None);
86        assert!(
87            parsed.errors.is_empty(),
88            "Should parse archive members in dependencies"
89        );
90
91        let makefile = parsed.root();
92        let rules: Vec<_> = makefile.rules().collect();
93        assert_eq!(rules.len(), 1);
94    }
95
96    #[test]
97    fn test_archive_member_with_variables() {
98        // Test archive members with variable references
99        let input = "$(LIB)($(OBJ)): $(SRC)\n\t$(CC) -c $(SRC)\n\t$(AR) r $(LIB) $(OBJ)\n";
100        let parsed = parse(input, None);
101        // Variable references in archive members should parse without errors
102        assert!(
103            parsed.errors.is_empty(),
104            "Should parse archive members with variables"
105        );
106    }
107
108    #[test]
109    fn test_archive_member_ast_access() {
110        // Test that we can access archive member nodes through the AST
111        let input = "libtest.a(foo.o bar.o): foo.c bar.c\n\tgcc -c foo.c bar.c\n";
112        let parsed = parse(input, None);
113        let makefile = parsed.root();
114
115        // Find archive member nodes in the syntax tree
116        let archive_member_count = makefile
117            .syntax()
118            .descendants()
119            .filter(|n| n.kind() == ARCHIVE_MEMBERS)
120            .count();
121
122        assert!(
123            archive_member_count > 0,
124            "Should find ARCHIVE_MEMBERS nodes in AST"
125        );
126    }
127}