1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
12pub struct NodeId(pub i64);
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
20pub struct FileId(pub u64);
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
28#[serde(transparent)]
29pub struct ErrorCode(pub u32);
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct SourceLoc {
34 pub offset: usize,
36 pub length: usize,
38 pub file_id: FileId,
40}
41
42impl SourceLoc {
43 pub fn parse(src: &str) -> Option<Self> {
47 let mut parts = src.split(':');
48 let offset = parts.next()?.parse::<usize>().ok()?;
49 let length = parts.next()?.parse::<usize>().ok()?;
50 let file_id = parts.next()?.parse::<u64>().ok()?;
51 if parts.next().is_some() {
53 return None;
54 }
55 Some(Self {
56 offset,
57 length,
58 file_id: FileId(file_id),
59 })
60 }
61
62 pub fn end(&self) -> usize {
64 self.offset + self.length
65 }
66
67 pub fn file_id_str(&self) -> SolcFileId {
70 SolcFileId::new(self.file_id.0.to_string())
71 }
72}
73
74impl std::fmt::Display for NodeId {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 write!(f, "{}", self.0)
77 }
78}
79
80impl std::fmt::Display for FileId {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 write!(f, "{}", self.0)
83 }
84}
85
86impl std::fmt::Display for ErrorCode {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 write!(f, "{}", self.0)
89 }
90}
91
92impl From<u32> for ErrorCode {
93 fn from(value: u32) -> Self {
94 Self(value)
95 }
96}
97
98impl std::borrow::Borrow<u32> for ErrorCode {
99 fn borrow(&self) -> &u32 {
100 &self.0
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
121#[serde(transparent)]
122pub struct AbsPath(String);
123
124impl AbsPath {
125 pub fn new(s: impl Into<String>) -> Self {
126 Self(s.into())
127 }
128 pub fn as_str(&self) -> &str {
129 &self.0
130 }
131 pub fn into_inner(self) -> String {
133 self.0
134 }
135}
136
137impl std::fmt::Display for AbsPath {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 f.write_str(&self.0)
140 }
141}
142
143impl AsRef<str> for AbsPath {
144 fn as_ref(&self) -> &str {
145 &self.0
146 }
147}
148
149impl std::ops::Deref for AbsPath {
150 type Target = str;
151 fn deref(&self) -> &Self::Target {
152 &self.0
153 }
154}
155
156impl AsRef<std::path::Path> for AbsPath {
157 fn as_ref(&self) -> &std::path::Path {
158 std::path::Path::new(&self.0)
159 }
160}
161
162impl From<String> for AbsPath {
163 fn from(s: String) -> Self {
164 Self(s)
165 }
166}
167
168impl From<&str> for AbsPath {
169 fn from(s: &str) -> Self {
170 Self(s.to_owned())
171 }
172}
173
174impl std::borrow::Borrow<str> for AbsPath {
175 fn borrow(&self) -> &str {
176 &self.0
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
185#[serde(transparent)]
186pub struct RelPath(String);
187
188impl RelPath {
189 pub fn new(s: impl Into<String>) -> Self {
190 Self(s.into())
191 }
192 pub fn as_str(&self) -> &str {
193 &self.0
194 }
195 pub fn into_inner(self) -> String {
196 self.0
197 }
198}
199
200impl std::fmt::Display for RelPath {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 f.write_str(&self.0)
203 }
204}
205
206impl AsRef<str> for RelPath {
207 fn as_ref(&self) -> &str {
208 &self.0
209 }
210}
211
212impl std::ops::Deref for RelPath {
213 type Target = str;
214 fn deref(&self) -> &Self::Target {
215 &self.0
216 }
217}
218
219impl From<String> for RelPath {
220 fn from(s: String) -> Self {
221 Self(s)
222 }
223}
224
225impl From<&str> for RelPath {
226 fn from(s: &str) -> Self {
227 Self(s.to_owned())
228 }
229}
230
231impl std::borrow::Borrow<str> for RelPath {
232 fn borrow(&self) -> &str {
233 &self.0
234 }
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
239#[serde(transparent)]
240pub struct SymbolName(String);
241
242impl SymbolName {
243 pub fn new(s: impl Into<String>) -> Self {
244 Self(s.into())
245 }
246 pub fn as_str(&self) -> &str {
247 &self.0
248 }
249}
250
251impl std::ops::Deref for SymbolName {
252 type Target = str;
253 fn deref(&self) -> &Self::Target {
254 &self.0
255 }
256}
257
258impl std::fmt::Display for SymbolName {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 f.write_str(&self.0)
261 }
262}
263
264impl From<String> for SymbolName {
265 fn from(s: String) -> Self {
266 Self(s)
267 }
268}
269
270impl From<&str> for SymbolName {
271 fn from(s: &str) -> Self {
272 Self(s.to_owned())
273 }
274}
275
276impl std::borrow::Borrow<str> for SymbolName {
277 fn borrow(&self) -> &str {
278 &self.0
279 }
280}
281
282impl std::borrow::Borrow<String> for SymbolName {
283 fn borrow(&self) -> &String {
284 &self.0
285 }
286}
287
288#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
290#[serde(transparent)]
291pub struct TypeIdentifier(String);
292
293impl TypeIdentifier {
294 pub fn new(s: impl Into<String>) -> Self {
295 Self(s.into())
296 }
297 pub fn as_str(&self) -> &str {
298 &self.0
299 }
300}
301
302impl std::ops::Deref for TypeIdentifier {
303 type Target = str;
304 fn deref(&self) -> &Self::Target {
305 &self.0
306 }
307}
308
309impl std::fmt::Display for TypeIdentifier {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 f.write_str(&self.0)
312 }
313}
314
315impl From<String> for TypeIdentifier {
316 fn from(s: String) -> Self {
317 Self(s)
318 }
319}
320
321impl From<&str> for TypeIdentifier {
322 fn from(s: &str) -> Self {
323 Self(s.to_owned())
324 }
325}
326
327impl std::borrow::Borrow<str> for TypeIdentifier {
328 fn borrow(&self) -> &str {
329 &self.0
330 }
331}
332
333impl std::borrow::Borrow<String> for TypeIdentifier {
334 fn borrow(&self) -> &String {
335 &self.0
336 }
337}
338
339#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
347#[serde(transparent)]
348pub struct SolcFileId(String);
349
350impl SolcFileId {
351 pub fn new(s: impl Into<String>) -> Self {
352 Self(s.into())
353 }
354 pub fn as_str(&self) -> &str {
355 &self.0
356 }
357 pub fn into_inner(self) -> String {
358 self.0
359 }
360}
361
362impl std::fmt::Display for SolcFileId {
363 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
364 f.write_str(&self.0)
365 }
366}
367
368impl AsRef<str> for SolcFileId {
369 fn as_ref(&self) -> &str {
370 &self.0
371 }
372}
373
374impl std::ops::Deref for SolcFileId {
375 type Target = str;
376 fn deref(&self) -> &Self::Target {
377 &self.0
378 }
379}
380
381impl From<String> for SolcFileId {
382 fn from(s: String) -> Self {
383 Self(s)
384 }
385}
386
387impl From<&str> for SolcFileId {
388 fn from(s: &str) -> Self {
389 Self(s.to_owned())
390 }
391}
392
393impl std::borrow::Borrow<str> for SolcFileId {
394 fn borrow(&self) -> &str {
395 &self.0
396 }
397}
398
399#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
410#[serde(transparent)]
411pub struct SrcLocation(String);
412
413impl SrcLocation {
414 pub fn new(s: impl Into<String>) -> Self {
415 Self(s.into())
416 }
417 pub fn as_str(&self) -> &str {
418 &self.0
419 }
420 pub fn into_inner(self) -> String {
421 self.0
422 }
423 pub fn parse(&self) -> Option<SourceLoc> {
425 SourceLoc::parse(&self.0)
426 }
427}
428
429impl std::fmt::Display for SrcLocation {
430 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
431 f.write_str(&self.0)
432 }
433}
434
435impl AsRef<str> for SrcLocation {
436 fn as_ref(&self) -> &str {
437 &self.0
438 }
439}
440
441impl std::ops::Deref for SrcLocation {
442 type Target = str;
443 fn deref(&self) -> &Self::Target {
444 &self.0
445 }
446}
447
448impl PartialEq<&str> for SrcLocation {
449 fn eq(&self, other: &&str) -> bool {
450 self.0 == *other
451 }
452}
453
454impl PartialEq<SrcLocation> for &str {
455 fn eq(&self, other: &SrcLocation) -> bool {
456 *self == other.0
457 }
458}
459
460impl From<String> for SrcLocation {
461 fn from(s: String) -> Self {
462 Self(s)
463 }
464}
465
466impl From<&str> for SrcLocation {
467 fn from(s: &str) -> Self {
468 Self(s.to_owned())
469 }
470}
471
472impl std::borrow::Borrow<str> for SrcLocation {
473 fn borrow(&self) -> &str {
474 &self.0
475 }
476}
477
478#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
484#[serde(transparent)]
485pub struct DocumentUri(String);
486
487impl DocumentUri {
488 pub fn new(s: impl Into<String>) -> Self {
489 Self(s.into())
490 }
491 pub fn as_str(&self) -> &str {
492 &self.0
493 }
494 pub fn into_inner(self) -> String {
495 self.0
496 }
497}
498
499impl std::fmt::Display for DocumentUri {
500 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501 f.write_str(&self.0)
502 }
503}
504
505impl AsRef<str> for DocumentUri {
506 fn as_ref(&self) -> &str {
507 &self.0
508 }
509}
510
511impl std::ops::Deref for DocumentUri {
512 type Target = str;
513 fn deref(&self) -> &Self::Target {
514 &self.0
515 }
516}
517
518impl From<String> for DocumentUri {
519 fn from(s: String) -> Self {
520 Self(s)
521 }
522}
523
524impl From<&str> for DocumentUri {
525 fn from(s: &str) -> Self {
526 Self(s.to_owned())
527 }
528}
529
530impl From<&String> for DocumentUri {
531 fn from(s: &String) -> Self {
532 Self(s.clone())
533 }
534}
535
536impl std::borrow::Borrow<str> for DocumentUri {
537 fn borrow(&self) -> &str {
538 &self.0
539 }
540}
541
542impl std::borrow::Borrow<String> for DocumentUri {
543 fn borrow(&self) -> &String {
544 &self.0
545 }
546}
547
548#[derive(Debug, Clone, PartialEq, Eq, Hash)]
564pub struct FuncSelector(String);
565
566impl FuncSelector {
567 pub fn new(hex: impl Into<String>) -> Self {
569 Self(hex.into())
570 }
571
572 pub fn as_hex(&self) -> &str {
574 &self.0
575 }
576
577 pub fn to_prefixed(&self) -> String {
579 format!("0x{}", self.0)
580 }
581}
582
583impl std::fmt::Display for FuncSelector {
584 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
585 write!(f, "{}", self.0)
586 }
587}
588
589#[derive(Debug, Clone, PartialEq, Eq, Hash)]
599pub struct EventSelector(String);
600
601impl EventSelector {
602 pub fn new(hex: impl Into<String>) -> Self {
604 Self(hex.into())
605 }
606
607 pub fn as_hex(&self) -> &str {
609 &self.0
610 }
611
612 pub fn to_prefixed(&self) -> String {
614 format!("0x{}", self.0)
615 }
616}
617
618impl std::fmt::Display for EventSelector {
619 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
620 write!(f, "{}", self.0)
621 }
622}
623
624#[derive(Debug, Clone, PartialEq, Eq, Hash)]
630pub enum Selector {
631 Func(FuncSelector),
633 Event(EventSelector),
635}
636
637impl Selector {
638 pub fn as_hex(&self) -> &str {
640 match self {
641 Selector::Func(s) => s.as_hex(),
642 Selector::Event(s) => s.as_hex(),
643 }
644 }
645
646 pub fn to_prefixed(&self) -> String {
648 match self {
649 Selector::Func(s) => s.to_prefixed(),
650 Selector::Event(s) => s.to_prefixed(),
651 }
652 }
653}
654
655impl std::fmt::Display for Selector {
656 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
657 match self {
658 Selector::Func(s) => write!(f, "{s}"),
659 Selector::Event(s) => write!(f, "{s}"),
660 }
661 }
662}
663
664#[derive(Debug, Clone, PartialEq, Eq, Hash)]
675pub struct MethodId(String);
676
677impl MethodId {
678 pub fn new(sig: impl Into<String>) -> Self {
680 Self(sig.into())
681 }
682
683 pub fn as_str(&self) -> &str {
685 &self.0
686 }
687
688 pub fn name(&self) -> &str {
690 self.0.split('(').next().unwrap_or(&self.0)
691 }
692}
693
694impl std::fmt::Display for MethodId {
695 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
696 write!(f, "{}", self.0)
697 }
698}
699
700#[derive(Debug, Clone)]
719pub struct PathInterner {
720 paths: Vec<String>,
722 path_to_id: HashMap<String, u64>,
724}
725
726impl PathInterner {
727 pub fn new() -> Self {
729 Self {
730 paths: Vec::new(),
731 path_to_id: HashMap::new(),
732 }
733 }
734
735 pub fn intern(&mut self, path: &str) -> FileId {
740 if let Some(&id) = self.path_to_id.get(path) {
741 return FileId(id);
742 }
743 let id = self.paths.len() as u64;
744 self.paths.push(path.to_owned());
745 self.path_to_id.insert(path.to_owned(), id);
746 FileId(id)
747 }
748
749 pub fn resolve(&self, id: FileId) -> Option<&str> {
751 self.paths.get(id.0 as usize).map(|s| s.as_str())
752 }
753
754 pub fn len(&self) -> usize {
756 self.paths.len()
757 }
758
759 pub fn is_empty(&self) -> bool {
761 self.paths.is_empty()
762 }
763
764 pub fn build_remap(
773 &mut self,
774 solc_id_to_path: &HashMap<SolcFileId, String>,
775 ) -> HashMap<u64, FileId> {
776 let mut remap = HashMap::with_capacity(solc_id_to_path.len());
777 for (solc_id, path) in solc_id_to_path {
778 let solc_num: u64 = solc_id.as_str().parse().unwrap_or(u64::MAX);
779 let canonical = self.intern(path);
780 remap.insert(solc_num, canonical);
781 }
782 remap
783 }
784
785 pub fn to_id_to_path_map(&self) -> HashMap<SolcFileId, String> {
790 self.paths
791 .iter()
792 .enumerate()
793 .map(|(i, path)| (SolcFileId::new(i.to_string()), path.clone()))
794 .collect()
795 }
796}
797
798impl Default for PathInterner {
799 fn default() -> Self {
800 Self::new()
801 }
802}
803
804#[cfg(test)]
805mod tests {
806 use super::*;
807
808 #[test]
809 fn test_source_loc_parse_valid() {
810 let loc = SourceLoc::parse("100:50:3").unwrap();
811 assert_eq!(loc.offset, 100);
812 assert_eq!(loc.length, 50);
813 assert_eq!(loc.file_id, FileId(3));
814 assert_eq!(loc.end(), 150);
815 assert_eq!(loc.file_id_str(), SolcFileId::new("3"));
816 }
817
818 #[test]
819 fn test_source_loc_parse_zero() {
820 let loc = SourceLoc::parse("0:0:0").unwrap();
821 assert_eq!(loc.offset, 0);
822 assert_eq!(loc.length, 0);
823 assert_eq!(loc.file_id, FileId(0));
824 }
825
826 #[test]
827 fn test_source_loc_parse_invalid_format() {
828 assert!(SourceLoc::parse("").is_none());
829 assert!(SourceLoc::parse("100").is_none());
830 assert!(SourceLoc::parse("100:50").is_none());
831 assert!(SourceLoc::parse("abc:50:3").is_none());
832 assert!(SourceLoc::parse("100:abc:3").is_none());
833 assert!(SourceLoc::parse("100:50:abc").is_none());
834 }
835
836 #[test]
837 fn test_source_loc_parse_rejects_extra_parts() {
838 assert!(SourceLoc::parse("100:50:3:extra").is_none());
839 }
840
841 #[test]
842 fn test_node_id_equality() {
843 assert_eq!(NodeId(42), NodeId(42));
844 assert_ne!(NodeId(42), NodeId(43));
845 }
846
847 #[test]
848 fn test_file_id_equality() {
849 assert_eq!(FileId(1), FileId(1));
850 assert_ne!(FileId(1), FileId(2));
851 }
852
853 #[test]
854 fn test_node_id_file_id_are_different_types() {
855 let _n: NodeId = NodeId(1);
859 let _f: FileId = FileId(1);
860 }
863
864 #[test]
867 fn test_func_selector_display() {
868 let sel = FuncSelector::new("f3cd914c");
869 assert_eq!(sel.as_hex(), "f3cd914c");
870 assert_eq!(sel.to_prefixed(), "0xf3cd914c");
871 assert_eq!(format!("{sel}"), "f3cd914c");
872 }
873
874 #[test]
875 fn test_func_selector_equality() {
876 assert_eq!(FuncSelector::new("f3cd914c"), FuncSelector::new("f3cd914c"));
877 assert_ne!(FuncSelector::new("f3cd914c"), FuncSelector::new("8da5cb5b"));
878 }
879
880 #[test]
881 fn test_event_selector_display() {
882 let sel =
883 EventSelector::new("8be0079c5114abcdef1234567890abcdef1234567890abcdef1234567890abcd");
884 assert_eq!(sel.as_hex().len(), 64);
885 assert!(sel.to_prefixed().starts_with("0x"));
886 }
887
888 #[test]
889 fn test_selector_enum_variants() {
890 let func = Selector::Func(FuncSelector::new("f3cd914c"));
891 let event = Selector::Event(EventSelector::new("a".repeat(64)));
892
893 assert_eq!(func.as_hex(), "f3cd914c");
894 assert_eq!(func.to_prefixed(), "0xf3cd914c");
895 assert_eq!(event.as_hex().len(), 64);
896 }
897
898 #[test]
899 fn test_method_id() {
900 let mid = MethodId::new(
901 "swap((address,address,uint24,int24,address),(bool,int256,uint160),bytes)",
902 );
903 assert_eq!(mid.name(), "swap");
904 assert!(mid.as_str().starts_with("swap("));
905 }
906
907 #[test]
908 fn test_method_id_no_params() {
909 let mid = MethodId::new("settle()");
910 assert_eq!(mid.name(), "settle");
911 }
912
913 #[test]
914 fn test_func_selector_hashmap_key() {
915 use std::collections::HashMap;
916 let mut map = HashMap::new();
917 map.insert(FuncSelector::new("f3cd914c"), "swap");
918 map.insert(FuncSelector::new("8da5cb5b"), "owner");
919 assert_eq!(map.get(&FuncSelector::new("f3cd914c")), Some(&"swap"));
920 assert_eq!(map.get(&FuncSelector::new("8da5cb5b")), Some(&"owner"));
921 }
922
923 #[test]
926 fn test_path_interner_basic() {
927 let mut interner = PathInterner::new();
928 assert!(interner.is_empty());
929
930 let id_a = interner.intern("src/Foo.sol");
931 let id_b = interner.intern("src/Bar.sol");
932 assert_ne!(id_a, id_b);
933 assert_eq!(interner.len(), 2);
934
935 let id_a2 = interner.intern("src/Foo.sol");
937 assert_eq!(id_a, id_a2);
938 assert_eq!(interner.len(), 2);
939 }
940
941 #[test]
942 fn test_path_interner_resolve() {
943 let mut interner = PathInterner::new();
944 let id = interner.intern("/abs/src/Pool.sol");
945 assert_eq!(interner.resolve(id), Some("/abs/src/Pool.sol"));
946 assert_eq!(interner.resolve(FileId(999)), None);
947 }
948
949 #[test]
950 fn test_path_interner_monotonic_ids() {
951 let mut interner = PathInterner::new();
952 let a = interner.intern("a.sol");
953 let b = interner.intern("b.sol");
954 let c = interner.intern("c.sol");
955 assert_eq!(a, FileId(0));
956 assert_eq!(b, FileId(1));
957 assert_eq!(c, FileId(2));
958 }
959
960 #[test]
961 fn test_path_interner_build_remap() {
962 let mut interner = PathInterner::new();
963 interner.intern("/abs/src/Foo.sol");
965
966 let mut solc_map = HashMap::new();
968 solc_map.insert(SolcFileId::new("0"), "/abs/src/Bar.sol".to_string());
969 solc_map.insert(SolcFileId::new("1"), "/abs/src/Foo.sol".to_string());
970
971 let remap = interner.build_remap(&solc_map);
972
973 assert_eq!(remap[&1], FileId(0));
975 assert_eq!(remap[&0], FileId(1));
977 }
978
979 #[test]
980 fn test_path_interner_to_id_to_path_map() {
981 let mut interner = PathInterner::new();
982 interner.intern("src/A.sol");
983 interner.intern("src/B.sol");
984
985 let map = interner.to_id_to_path_map();
986 assert_eq!(map.get("0").map(|s| s.as_str()), Some("src/A.sol"));
987 assert_eq!(map.get("1").map(|s| s.as_str()), Some("src/B.sol"));
988 }
989}