1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::fmt;
4
5use crate::checker::Checker;
6use crate::langs::*;
7use crate::node::Node;
8use crate::*;
9
10#[derive(Clone, Debug, Default)]
15pub struct Stats {
16 class_npm: usize,
17 interface_npm: usize,
18 class_nm: usize,
19 interface_nm: usize,
20 class_npm_sum: usize,
21 interface_npm_sum: usize,
22 class_nm_sum: usize,
23 interface_nm_sum: usize,
24 is_class_space: bool,
25}
26
27impl Serialize for Stats {
28 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
29 where
30 S: Serializer,
31 {
32 let mut st = serializer.serialize_struct("npm", 9)?;
33 st.serialize_field("classes", &self.class_npm_sum())?;
34 st.serialize_field("interfaces", &self.interface_npm_sum())?;
35 st.serialize_field("class_methods", &self.class_nm_sum())?;
36 st.serialize_field("interface_methods", &self.interface_nm_sum())?;
37 st.serialize_field("classes_average", &self.class_coa())?;
38 st.serialize_field("interfaces_average", &self.interface_coa())?;
39 st.serialize_field("total", &self.total_npm())?;
40 st.serialize_field("total_methods", &self.total_nm())?;
41 st.serialize_field("average", &self.total_coa())?;
42 st.end()
43 }
44}
45
46impl fmt::Display for Stats {
47 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48 write!(
49 f,
50 "classes: {}, interfaces: {}, class_methods: {}, interface_methods: {}, classes_average: {}, interfaces_average: {}, total: {}, total_methods: {}, average: {}",
51 self.class_npm_sum(),
52 self.interface_npm_sum(),
53 self.class_nm_sum(),
54 self.interface_nm_sum(),
55 self.class_coa(),
56 self.interface_coa(),
57 self.total_npm(),
58 self.total_nm(),
59 self.total_coa()
60 )
61 }
62}
63
64impl Stats {
65 pub fn merge(&mut self, other: &Stats) {
67 self.class_npm_sum += other.class_npm_sum;
68 self.interface_npm_sum += other.interface_npm_sum;
69 self.class_nm_sum += other.class_nm_sum;
70 self.interface_nm_sum += other.interface_nm_sum;
71 }
72
73 #[inline(always)]
75 pub fn class_npm(&self) -> f64 {
76 self.class_npm as f64
77 }
78
79 #[inline(always)]
81 pub fn interface_npm(&self) -> f64 {
82 self.interface_npm as f64
83 }
84
85 #[inline(always)]
87 pub fn class_nm(&self) -> f64 {
88 self.class_nm as f64
89 }
90
91 #[inline(always)]
93 pub fn interface_nm(&self) -> f64 {
94 self.interface_nm as f64
95 }
96
97 #[inline(always)]
99 pub fn class_npm_sum(&self) -> f64 {
100 self.class_npm_sum as f64
101 }
102
103 #[inline(always)]
105 pub fn interface_npm_sum(&self) -> f64 {
106 self.interface_npm_sum as f64
107 }
108
109 #[inline(always)]
111 pub fn class_nm_sum(&self) -> f64 {
112 self.class_nm_sum as f64
113 }
114
115 #[inline(always)]
117 pub fn interface_nm_sum(&self) -> f64 {
118 self.interface_nm_sum as f64
119 }
120
121 #[inline(always)]
131 pub fn class_coa(&self) -> f64 {
132 self.class_npm_sum() / self.class_nm_sum()
133 }
134
135 #[inline(always)]
145 pub fn interface_coa(&self) -> f64 {
146 if self.interface_npm_sum == self.interface_nm_sum && self.interface_npm_sum != 0 {
149 1.0
150 } else {
151 self.interface_npm_sum() / self.interface_nm_sum()
152 }
153 }
154
155 #[inline(always)]
165 pub fn total_coa(&self) -> f64 {
166 self.total_npm() / self.total_nm()
167 }
168
169 #[inline(always)]
171 pub fn total_npm(&self) -> f64 {
172 self.class_npm_sum() + self.interface_npm_sum()
173 }
174
175 #[inline(always)]
177 pub fn total_nm(&self) -> f64 {
178 self.class_nm_sum() + self.interface_nm_sum()
179 }
180
181 #[inline(always)]
184 pub(crate) fn compute_sum(&mut self) {
185 self.class_npm_sum += self.class_npm;
186 self.interface_npm_sum += self.interface_npm;
187 self.class_nm_sum += self.class_nm;
188 self.interface_nm_sum += self.interface_nm;
189 }
190
191 #[inline(always)]
193 pub(crate) fn is_disabled(&self) -> bool {
194 !self.is_class_space
195 }
196}
197
198#[doc(hidden)]
199pub trait Npm
200where
201 Self: Checker,
202{
203 fn compute(_node: &Node, _stats: &mut Stats) {}
204}
205
206impl Npm for PythonCode {}
207impl Npm for MozjsCode {}
208impl Npm for JavascriptCode {}
209impl Npm for TypescriptCode {}
210impl Npm for TsxCode {}
211impl Npm for RustCode {}
212impl Npm for CppCode {}
213impl Npm for PreprocCode {}
214impl Npm for CcommentCode {}
215
216impl Npm for JavaCode {
217 fn compute(node: &Node, stats: &mut Stats) {
218 use Java::*;
219
220 if Self::is_func_space(node) && stats.is_disabled() {
222 stats.is_class_space = true;
223 }
224
225 match node.object().kind_id().into() {
226 ClassBody => {
227 stats.class_nm += node
228 .children()
229 .filter(|node| Self::is_func(node))
230 .map(|method| {
231 if let Some(modifiers) = method.object().child(0) {
235 if matches!(modifiers.kind_id().into(), Modifiers)
237 && Node::new(modifiers)
238 .first_child(|id| id == Public)
239 .is_some()
240 {
241 stats.class_npm += 1;
242 }
243 }
244 })
245 .count();
246 }
247 InterfaceBody => {
250 stats.interface_nm += node.children().filter(|node| Self::is_func(node)).count();
254 stats.interface_npm = stats.interface_nm;
255 }
256 _ => {}
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use std::path::PathBuf;
264
265 use super::*;
266
267 #[test]
268 fn java_constructors() {
269 check_metrics!(
270 "class X {
271 X() {}
272 private X(int a) {}
273 protected X(int a, int b) {}
274 public X(int a, int b, int c) {} // +1
275 }",
276 "foo.java",
277 JavaParser,
278 npm,
279 [
280 (class_npm_sum, 1, usize),
281 (interface_npm_sum, 0, usize),
282 (class_nm_sum, 4, usize),
283 (interface_nm_sum, 0, usize),
284 (total_npm, 1, usize),
285 (total_nm, 4, usize)
286 ],
287 [
288 (class_coa, 0.25),
289 (interface_coa, f64::NAN),
290 (total_coa, 0.25)
291 ]
292 );
293 }
294
295 #[test]
296 fn java_methods_returning_primitive_types() {
297 check_metrics!(
298 "class X {
299 public byte a() {} // +1
300 public short b() {} // +1
301 public int c() {} // +1
302 public long d() {} // +1
303 public float e() {} // +1
304 public double f() {} // +1
305 public boolean g() {} // +1
306 public char h() {} // +1
307 byte i() {}
308 short j() {}
309 int k() {}
310 long l() {}
311 float m() {}
312 double n() {}
313 boolean o() {}
314 char p() {}
315 }",
316 "foo.java",
317 JavaParser,
318 npm,
319 [
320 (class_npm_sum, 8, usize),
321 (interface_npm_sum, 0, usize),
322 (class_nm_sum, 16, usize),
323 (interface_nm_sum, 0, usize),
324 (total_npm, 8, usize),
325 (total_nm, 16, usize)
326 ],
327 [
328 (class_coa, 0.5),
329 (interface_coa, f64::NAN),
330 (total_coa, 0.5)
331 ]
332 );
333 }
334
335 #[test]
336 fn java_methods_returning_arrays() {
337 check_metrics!(
338 "class X {
339 public byte[] a() {} // +1
340 public short[] b() {} // +1
341 public int[] c() {} // +1
342 public long[] d() {} // +1
343 public float[] e() {} // +1
344 public double[] f() {} // +1
345 public boolean[] g() {} // +1
346 public char[] h() {} // +1
347 byte[] i() {}
348 short[] j() {}
349 int[] k() {}
350 long[] l() {}
351 float[] m() {}
352 double[] n() {}
353 boolean[] o() {}
354 char[] p() {}
355 }",
356 "foo.java",
357 JavaParser,
358 npm,
359 [
360 (class_npm_sum, 8, usize),
361 (interface_npm_sum, 0, usize),
362 (class_nm_sum, 16, usize),
363 (interface_nm_sum, 0, usize),
364 (total_npm, 8, usize),
365 (total_nm, 16, usize)
366 ],
367 [
368 (class_coa, 0.5),
369 (interface_coa, f64::NAN),
370 (total_coa, 0.5)
371 ]
372 );
373 }
374
375 #[test]
376 fn java_methods_returning_objects() {
377 check_metrics!(
378 "class X {
379 public Integer[] a() {} // +1
380 public Integer b() {} // +1
381 public String[] c() {} // +1
382 public String d() {} // +1
383 public Y[] e() {} // +1
384 public Y f() {} // +1
385 Integer[] g() {}
386 Integer h() {}
387 String[] i() {}
388 String j() {}
389 Y[] k() {}
390 Y l() {}
391 }",
392 "foo.java",
393 JavaParser,
394 npm,
395 [
396 (class_npm_sum, 6, usize),
397 (interface_npm_sum, 0, usize),
398 (class_nm_sum, 12, usize),
399 (interface_nm_sum, 0, usize),
400 (total_npm, 6, usize),
401 (total_nm, 12, usize)
402 ],
403 [
404 (class_coa, 0.5),
405 (interface_coa, f64::NAN),
406 (total_coa, 0.5)
407 ]
408 );
409 }
410
411 #[test]
412 fn java_methods_with_generic_types() {
413 check_metrics!(
414 "class X {
415 public <T, S extends T> void a(T x, S y) {} // +1
416 public <T, S> int b(T x, S y) {} // +1
417 public <T> boolean c(T x) {} // +1
418 public <T> ArrayList<T> d() {} // +1
419 public Y<String> e() {} // +1
420 <T, S extends T> void f(T x, S y) {}
421 <T, S> int g(T x, S y) {}
422 <T> boolean h(T x) {}
423 <T> ArrayList<T> i() {}
424 Y<String> j() {}
425 }",
426 "foo.java",
427 JavaParser,
428 npm,
429 [
430 (class_npm_sum, 5, usize),
431 (interface_npm_sum, 0, usize),
432 (class_nm_sum, 10, usize),
433 (interface_nm_sum, 0, usize),
434 (total_npm, 5, usize),
435 (total_nm, 10, usize)
436 ],
437 [
438 (class_coa, 0.5),
439 (interface_coa, f64::NAN),
440 (total_coa, 0.5)
441 ]
442 );
443 }
444
445 #[test]
446 fn java_method_modifiers() {
447 check_metrics!(
448 "abstract class X {
449 public static final synchronized strictfp void a() {} // +1
450 static public final synchronized strictfp void b() {} // +1
451 static final public synchronized strictfp void c() {} // +1
452 static final synchronized public strictfp void d() {} // +1
453 static final synchronized strictfp public void e() {} // +1
454 protected static final synchronized native void f();
455 static protected final synchronized native void g();
456 static final protected synchronized native void h();
457 static final synchronized protected native void i();
458 static final synchronized native protected void j();
459 abstract public void k(); // +1
460 abstract void l();
461 }",
462 "foo.java",
463 JavaParser,
464 npm,
465 [
466 (class_npm_sum, 6, usize),
467 (interface_npm_sum, 0, usize),
468 (class_nm_sum, 12, usize),
469 (interface_nm_sum, 0, usize),
470 (total_npm, 6, usize),
471 (total_nm, 12, usize)
472 ],
473 [
474 (class_coa, 0.5),
475 (interface_coa, f64::NAN),
476 (total_coa, 0.5)
477 ]
478 );
479 }
480
481 #[test]
482 fn java_classes() {
483 check_metrics!(
484 "class X {
485 public void a() {} // +1
486 public void b() {} // +1
487 private void c() {}
488 }
489 class Y {
490 private void d() {}
491 private void e() {}
492 public void f() {} // +1
493 }",
494 "foo.java",
495 JavaParser,
496 npm,
497 [
498 (class_npm_sum, 3, usize),
499 (interface_npm_sum, 0, usize),
500 (class_nm_sum, 6, usize),
501 (interface_nm_sum, 0, usize),
502 (total_npm, 3, usize),
503 (total_nm, 6, usize)
504 ],
505 [
506 (class_coa, 0.5),
507 (interface_coa, f64::NAN),
508 (total_coa, 0.5)
509 ]
510 );
511 }
512
513 #[test]
514 fn java_nested_inner_classes() {
515 check_metrics!(
516 "class X {
517 public void a() {} // +1
518 class Y {
519 public void b() {} // +1
520 class Z {
521 public void c() {} // +1
522 }
523 }
524 }",
525 "foo.java",
526 JavaParser,
527 npm,
528 [
529 (class_npm_sum, 3, usize),
530 (interface_npm_sum, 0, usize),
531 (class_nm_sum, 3, usize),
532 (interface_nm_sum, 0, usize),
533 (total_npm, 3, usize),
534 (total_nm, 3, usize)
535 ],
536 [
537 (class_coa, 1.0),
538 (interface_coa, f64::NAN),
539 (total_coa, 1.0)
540 ]
541 );
542 }
543
544 #[test]
545 fn java_local_inner_classes() {
546 check_metrics!(
547 "class X {
548 public void a() { // +1
549 class Y {
550 public void b() { // +1
551 class Z {
552 public void c() {} // +1
553 }
554 }
555 }
556 }
557 }",
558 "foo.java",
559 JavaParser,
560 npm,
561 [
562 (class_npm_sum, 3, usize),
563 (interface_npm_sum, 0, usize),
564 (class_nm_sum, 3, usize),
565 (interface_nm_sum, 0, usize),
566 (total_npm, 3, usize),
567 (total_nm, 3, usize)
568 ],
569 [
570 (class_coa, 1.0),
571 (interface_coa, f64::NAN),
572 (total_coa, 1.0)
573 ]
574 );
575 }
576
577 #[test]
578 fn java_anonymous_inner_classes() {
579 check_metrics!(
580 "abstract class X {
581 public abstract void a(); // +1
582 }
583 abstract class Y {
584 abstract void b();
585 }
586 class Z {
587 public void c(){ // +1
588 X x = new X() {
589 @Override
590 public void a() {} // +1
591 };
592 Y y = new Y() {
593 @Override
594 void b() {}
595 };
596 }
597 }",
598 "foo.java",
599 JavaParser,
600 npm,
601 [
602 (class_npm_sum, 3, usize),
603 (interface_npm_sum, 0, usize),
604 (class_nm_sum, 5, usize),
605 (interface_nm_sum, 0, usize),
606 (total_npm, 3, usize),
607 (total_nm, 5, usize)
608 ],
609 [
610 (class_coa, 0.6),
611 (interface_coa, f64::NAN),
612 (total_coa, 0.6)
613 ]
614 );
615 }
616
617 #[test]
618 fn java_interface() {
619 check_metrics!(
620 "interface X {
621 public int a(); // +1
622 boolean b(); // +1
623 void c(); // +1
624 }",
625 "foo.java",
626 JavaParser,
627 npm,
628 [
629 (class_npm_sum, 0, usize),
630 (interface_npm_sum, 3, usize),
631 (class_nm_sum, 0, usize),
632 (interface_nm_sum, 3, usize),
633 (total_npm, 3, usize),
634 (total_nm, 3, usize)
635 ],
636 [
637 (class_coa, f64::NAN),
638 (interface_coa, 1.0),
639 (total_coa, 1.0)
640 ]
641 );
642 }
643
644 #[test]
645 fn java_interfaces_and_class() {
646 check_metrics!(
647 "interface X {
648 void a(); // +1
649 }
650 interface Y extends X {
651 void b(); // +1
652 void c(); // +1
653 }
654 class Z implements Y {
655 @Override
656 public void a() {} // +1
657 @Override
658 public void b() {} // +1
659 @Override
660 public void c() {} // +1
661 void d() {}
662 void e() {}
663 }",
664 "foo.java",
665 JavaParser,
666 npm,
667 [
668 (class_npm_sum, 3, usize),
669 (interface_npm_sum, 3, usize),
670 (class_nm_sum, 5, usize),
671 (interface_nm_sum, 3, usize),
672 (total_npm, 6, usize),
673 (total_nm, 8, usize)
674 ],
675 [(class_coa, 0.6), (interface_coa, 1.0), (total_coa, 0.75)]
676 );
677 }
678}