• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gripmock / grpctestify-rust / 24905977248

24 Apr 2026 06:40PM UTC coverage: 78.024% (+0.3%) from 77.729%
24905977248

Pull #43

github

web-flow
Merge fca25a9f8 into 017e47d15
Pull Request #43: new command gen grpcurl & call

741 of 993 new or added lines in 24 files covered. (74.62%)

2 existing lines in 2 files now uncovered.

19595 of 25114 relevant lines covered (78.02%)

39580.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.76
/src/parser/builder.rs
1
// Builder utilities for constructing .gctf documents programmatically.
2

3
use std::collections::HashMap;
4

5
use serde_json::Value;
6

7
use super::ast::{
8
    DocumentMetadata, FileMeta, GctfDocument, InlineOptions, Section, SectionContent, SectionType,
9
};
10

11
#[derive(Debug, Clone)]
12
pub struct GctfDocumentBuilder {
13
    file_path: String,
14
    sections: Vec<Section>,
15
}
16

17
impl GctfDocumentBuilder {
18
    pub fn new() -> Self {
11✔
19
        Self {
11✔
20
            file_path: String::new(),
11✔
21
            sections: Vec::new(),
11✔
22
        }
11✔
23
    }
11✔
24

NEW
25
    pub fn with_file_path(mut self, file_path: impl Into<String>) -> Self {
×
NEW
26
        self.file_path = file_path.into();
×
NEW
27
        self
×
NEW
28
    }
×
29

30
    pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
11✔
31
        self.push_section(
11✔
32
            SectionType::Endpoint,
11✔
33
            SectionContent::Single(endpoint.into()),
11✔
34
        );
35
        self
11✔
36
    }
11✔
37

38
    pub fn address(mut self, address: impl Into<String>) -> Self {
11✔
39
        self.push_section(SectionType::Address, SectionContent::Single(address.into()));
11✔
40
        self
11✔
41
    }
11✔
42

43
    pub fn request_headers(mut self, headers: HashMap<String, String>) -> Self {
10✔
44
        if !headers.is_empty() {
10✔
45
            self.push_section(
3✔
46
                SectionType::RequestHeaders,
3✔
47
                SectionContent::KeyValues(headers),
3✔
48
            );
3✔
49
        }
7✔
50
        self
10✔
51
    }
10✔
52

53
    pub fn request(mut self, request: Value) -> Self {
11✔
54
        self.push_section(SectionType::Request, SectionContent::Json(request));
11✔
55
        self
11✔
56
    }
11✔
57

58
    pub fn response(mut self, response: Value) -> Self {
1✔
59
        self.push_section(SectionType::Response, SectionContent::Json(response));
1✔
60
        self
1✔
61
    }
1✔
62

63
    pub fn error(mut self, error: impl Into<String>) -> Self {
1✔
64
        self.push_section(SectionType::Error, SectionContent::Single(error.into()));
1✔
65
        self
1✔
66
    }
1✔
67

NEW
68
    pub fn tls(mut self, tls: HashMap<String, String>) -> Self {
×
NEW
69
        if !tls.is_empty() {
×
NEW
70
            self.push_section(SectionType::Tls, SectionContent::KeyValues(tls));
×
NEW
71
        }
×
NEW
72
        self
×
NEW
73
    }
×
74

75
    pub fn options(mut self, options: HashMap<String, String>) -> Self {
10✔
76
        if !options.is_empty() {
10✔
77
            self.push_section(SectionType::Options, SectionContent::KeyValues(options));
1✔
78
        }
9✔
79
        self
10✔
80
    }
10✔
81

82
    pub fn proto(mut self, proto: HashMap<String, String>) -> Self {
2✔
83
        if !proto.is_empty() {
2✔
84
            self.push_section(SectionType::Proto, SectionContent::KeyValues(proto));
1✔
85
        }
1✔
86
        self
2✔
87
    }
2✔
88

NEW
89
    pub fn meta(mut self, meta: FileMeta) -> Self {
×
NEW
90
        if !meta.is_empty() {
×
NEW
91
            self.push_section(SectionType::Meta, SectionContent::Meta(meta));
×
NEW
92
        }
×
NEW
93
        self
×
NEW
94
    }
×
95

96
    pub fn build(self) -> GctfDocument {
11✔
97
        GctfDocument {
11✔
98
            file_path: self.file_path,
11✔
99
            sections: self.sections,
11✔
100
            metadata: DocumentMetadata {
11✔
101
                source: None,
11✔
102
                mtime: None,
11✔
103
                parsed_at: crate::time::now_timestamp(),
11✔
104
            },
11✔
105
            next_document: None,
11✔
106
        }
11✔
107
    }
11✔
108

109
    pub fn render(self) -> String {
11✔
110
        let doc = self.build();
11✔
111
        crate::serialize_gctf(&doc)
11✔
112
    }
11✔
113

114
    fn push_section(&mut self, section_type: SectionType, content: SectionContent) {
40✔
115
        self.sections.push(Section {
40✔
116
            section_type,
40✔
117
            content,
40✔
118
            inline_options: InlineOptions::default(),
40✔
119
            raw_content: String::new(),
40✔
120
            start_line: 0,
40✔
121
            end_line: 0,
40✔
122
        });
40✔
123
    }
40✔
124
}
125

126
impl Default for GctfDocumentBuilder {
NEW
127
    fn default() -> Self {
×
NEW
128
        Self::new()
×
NEW
129
    }
×
130
}
131

132
#[cfg(test)]
133
mod tests {
134
    use super::*;
135
    use serde_json::json;
136

137
    #[test]
138
    fn builder_renders_minimal_document() {
1✔
139
        let output = GctfDocumentBuilder::new()
1✔
140
            .address("localhost:4770")
1✔
141
            .endpoint("auth.AuthService/CheckAccess")
1✔
142
            .request(json!({"action": "delete"}))
1✔
143
            .render();
1✔
144

145
        assert!(output.contains("--- ADDRESS ---\nlocalhost:4770"));
1✔
146
        assert!(output.contains("--- ENDPOINT ---\nauth.AuthService/CheckAccess"));
1✔
147
        assert!(output.contains("--- REQUEST ---"));
1✔
148
    }
1✔
149

150
    #[test]
151
    fn builder_skips_empty_maps() {
1✔
152
        let output = GctfDocumentBuilder::new()
1✔
153
            .address("localhost:4770")
1✔
154
            .endpoint("svc/method")
1✔
155
            .request_headers(HashMap::new())
1✔
156
            .options(HashMap::new())
1✔
157
            .proto(HashMap::new())
1✔
158
            .request(json!({}))
1✔
159
            .render();
1✔
160

161
        assert!(!output.contains("REQUEST_HEADERS"));
1✔
162
        assert!(!output.contains("OPTIONS"));
1✔
163
        assert!(!output.contains("PROTO"));
1✔
164
    }
1✔
165
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc