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

tamada / oinkie / 28728637318

05 Jul 2026 03:48AM UTC coverage: 74.24% (+1.8%) from 72.462%
28728637318

Pull #17

github

tamada
refactor: remove dead stub modules and commented-out code

src/llvm.rs and src/ninja.rs were identical private stubs whose every
method was unimplemented!(); nothing referenced them, and the
user-facing 'not yet implemented' errors for those lifters already
live in LifterBuilder::build. Keeping panicking stubs around invites
someone to wire them in and ship a runtime panic.

Also drop commented-out code blocks in birthmarks.rs, program.rs,
cli.rs and main.rs that git history already remembers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pull Request #17: refactor: unimplemented!() スタブとコメントアウトコードの除去(技術的負債)

143 of 178 new or added lines in 7 files covered. (80.34%)

3 existing lines in 2 files now uncovered.

1611 of 2170 relevant lines covered (74.24%)

12.31 hits per line

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

56.47
/cli/cli.rs
1
use std::path::{Path, PathBuf};
2

3
pub use crate::info::BType;
4
use clap::ValueEnum;
5
use oinkie::prelude::*;
6

7
#[derive(Debug, clap::Parser)]
8
#[command(version, about)]
9
pub struct OinkieOpts {
10
    #[clap(subcommand)]
11
    pub command: OinkieCommand,
12

13
    #[clap(short, long, value_enum, default_value_t = LogLevel::Warn, value_name = "LEVEL", ignore_case = true, help = "Log level for the application")]
14
    pub level: LogLevel,
15
}
16

17
impl OinkieOpts {
18
    pub fn init(&self) -> Result<()> {
9✔
19
        let filter = match self.level {
9✔
NEW
20
            LogLevel::Debug => log::LevelFilter::Debug,
×
NEW
21
            LogLevel::Info => log::LevelFilter::Info,
×
22
            LogLevel::Warn => log::LevelFilter::Warn,
9✔
NEW
23
            LogLevel::Error => log::LevelFilter::Error,
×
NEW
24
            LogLevel::Trace => log::LevelFilter::Trace,
×
NEW
25
            LogLevel::Off => log::LevelFilter::Off,
×
26
        };
27
        env_logger::Builder::new().filter_level(filter).init();
9✔
28
        Ok(())
9✔
29
    }
9✔
30
}
31

32
#[derive(Debug, clap::Parser, ValueEnum, Clone)]
33
pub enum LogLevel {
34
    Error,
35
    Warn,
36
    Info,
37
    Debug,
38
    Trace,
39
    Off,
40
}
41

42
#[derive(Debug, clap::Parser)]
43
pub enum OinkieCommand {
44
    #[command(name="info", about = "Display information about the application")]
45
    Info,
46

47
    #[command(name="lift", about = "Lift binary files to P-code JSON files using a specified lifter")]
48
    Lift(LiftOpts),
49

50
    #[command(name="extract", about = "Extract birthmarks from a lifted binary file (JSON format)")]
51
    Extract(ExtractOpts),
52

53
    #[command(name="compare", about = "Compare birthmarks and output the similarity score")]
54
    Compare(CompareOpts),
55

56
    #[command(name="reaggregate", about = "Reaggregate the element-wise similarity scores and recalculate the birthmark-wise similarity score")]
57
    Reaggregate(ReaggregateOpts),
58

59
    #[command(name="run", about = "Extract birthmarks and compare them in one command")]
60
    Run(RunOpts),
61

62
    #[cfg(debug_assertions)]
63
    #[command(name="gencomp", about = "Generate completions")]
64
    GenComp,
65
}
66

67
#[derive(Debug, clap::Parser)]
68
pub struct LiftOpts {
69
    #[clap(short, long, default_value = "pcodes", value_name = "DIRECTORY", help = "Specify the directory for putting the resultant JSON files for the lifted P-code (default: './pcodes' directory)")]
70
    dest: PathBuf,
71

72
    #[clap(short = 'l', long, value_enum, default_value_t = LifterType::Ghidra, help = "Specify the lifter type")]
73
    lifter_type: LifterType,
74

75
    #[clap(short = 'H', long, value_name = "HOME", help = "Specify the path to the home directory of the lifter (e.g., GHIDRA_HOME for Ghidra). If not specified, the environment variable (e.g., GHIDRA_HOME) or default paths are searched.")]
76
    home: Option<PathBuf>,
77

78
    #[clap(short = 'i', long = "intermediate", value_name = "DIRECTORY", help = "Directory to keep intermediate files like Ghidra project directories. If not specified, a temporary directory is used and deleted.")]
79
    intermediate_dir: Option<PathBuf>,
80

81
    #[clap(long, value_name = "SCRIPT", help = "Path to a custom lifting script. Interpretation depends on the lifter type. For Ghidra, it's the path to a Java script.")]
82
    script: Option<PathBuf>,
83

84
    #[clap(short = 'S', long, default_value_t = false, help = "Skip if the resultant JSON file already exists")]
85
    skip: bool,
86

87
    #[clap(index = 1, value_name = "FILES", help = "Path to the binary or intermediate files to lift")]
88
    files: Vec<PathBuf>,
89
}
90

91
impl LiftOpts {
92
    pub fn dest(&self) -> &Path {
2✔
93
        &self.dest
2✔
94
    }
2✔
95

96
    pub fn lifter_type(&self) -> LifterType {
1✔
97
        self.lifter_type
1✔
98
    }
1✔
99

100
    pub fn home(&self) -> Option<&Path> {
2✔
101
        self.home.as_deref()
2✔
102
    }
2✔
103

104
    pub fn intermediate_dir(&self) -> Option<&Path> {
1✔
105
        self.intermediate_dir.as_deref()
1✔
106
    }
1✔
107

108
    pub fn script(&self) -> Option<&Path> {
1✔
109
        self.script.as_deref()
1✔
110
    }
1✔
111

112
    pub fn is_skip(&self) -> bool {
1✔
113
        self.skip
1✔
114
    }
1✔
115

116
    pub fn iter(&self) -> impl Iterator<Item = &PathBuf> {
2✔
117
        self.files.iter()
2✔
118
    }
2✔
119

120
    pub fn len(&self) -> usize {
1✔
121
        self.files.len()
1✔
122
    }
1✔
123
}
124

125
#[derive(Debug, clap::Parser, ValueEnum, Clone)]
126
#[clap(rename_all = "kebab-case")]
127
pub enum BinaryType {
128
    Ghidra,
129
    Llvm,
130
    BinaryNinja,
131
}
132

133
#[derive(Debug, clap::Parser)]
134
pub struct ExtractOpts {
135
    #[clap(short, long, default_value = "birthmarks", value_name = "DIRECTORY", help = "Specify the directory for putting the resultant JSON files for the extracted birthmarks (default: './birthmarks' directory)")]
136
    dest: PathBuf,
137

138
    #[clap(short, long, value_enum, value_name = "BIRTHMARK_TYPE", default_value_t = BType::OpSeq, hide_possible_values = true, ignore_case = true, help = "Type of birthmark to extract.
139
fc (Function Calls) and op (Opcode) with set, seq, and freq variants are supported.
140
For example, 'op-seq' extracts the sequence of operations as a birthmark,
141
while 'fc-freq' extracts the frequency of function calls.
142
The full birthmark types cann be found by running 'oinkie info'.")]
143
    birthmark_type: BType,
144

145
    #[clap(short = 'S', long, default_value_t = false, help = "Skip the resultant birthmark file is already exists")]
146
    skip: bool,
147

148
    #[clap(short = 'B', long, value_enum, value_name = "BINARY_TYPE", default_value_t = BinaryType::Ghidra, ignore_case = true, help = "Type of binary. Current version only supports Ghidra JSON format")]
149
    binary_type: BinaryType,
150

151
    #[clap(index = 1, value_name = "JSON_FILES", help = "Path to the JSON files to extract birthmarks from")]
152
    files: Vec<PathBuf>,
153
}
154

155
impl ExtractOpts {
156
    pub fn dest(&self) -> &Path {
3✔
157
        &self.dest
3✔
158
    }
3✔
159

160
    pub fn extractor(&self) -> Extractor {
3✔
161
        Extractor::new(self.birthmark_type.clone().into())
3✔
162
    }
3✔
163

164
    pub fn len(&self) -> usize {
3✔
165
        self.files.len()
3✔
166
    }
3✔
167

168
    pub fn is_empty(&self) -> bool {
3✔
169
        self.files.is_empty()
3✔
170
    }
3✔
171

172
    pub fn iter(&self) -> impl Iterator<Item = &PathBuf> {
3✔
173
        self.files.iter()
3✔
174
    }
3✔
175

176
    pub fn is_skip(&self) -> bool {
6✔
177
        self.skip
6✔
178
    }
6✔
179
}
180

181
#[derive(Debug, clap::Parser)]
182
pub struct ReaggregateOpts {
183
    #[clap(
184
        short = 'A', long, default_value = "hungarian", value_name = "METHOD", ignore_case = true, 
185
        help = "Specify the aggregator for combining element-wise similarity scores into a birthmark-wise similarity score.
186
Available: 
187
- hungarian  Use the Hungarian algorithm to find the optimal matching between elements of two birthmarks,
188
             maximizing the total similarity score.
189
- topn:N     For each element in the first birthmark, consider only the top N most similar elements in the
190
             second birthmark when calculating the overall similarity score. This can reduce noise from less
191
             relevant matches and focus on the most significant similarities."
192
    )]
193
    aggregator: Aggregator,
194

195
    #[clap(short, long, value_name = "RESULT.CSV", help = "Specify the result CSV file of the comparing results to reaggregate.
196
The file contains the birthmark-wise similarity score list.", default_value = "reaggregate.csv")]
197
    dest_file: PathBuf,
198

199
    #[clap(index = 1, value_name = "SCORE_DIRECTORY", help = "Path to the directory containing the element-wise similarity scores")]
200
    score_directory: PathBuf,
201
}
202

203
impl ReaggregateOpts {
204
    pub fn aggregator(&self) -> &Aggregator {
1✔
205
        &self.aggregator
1✔
206
    }
1✔
207

208
    pub fn score_directory(&self) -> &Path {
1✔
209
        &self.score_directory
1✔
210
    }
1✔
211

212
    pub fn dest_file(&self) -> &PathBuf {
1✔
213
        &self.dest_file
1✔
214
    }
1✔
215
}
216

217
#[derive(Debug, clap::Parser)]
218
pub struct CompareOpts {
219
    #[clap(short, long, value_enum, default_value_t = Algorithm::Jaccard, value_name = "ALGORITHM", ignore_case = true, help = "Specify the similarity calculation algorithm.")]
220
    algorithm: Algorithm,    
221

222
    #[clap(
223
        short = 'A', long, default_value = "hungarian", value_name = "METHOD", ignore_case = true, 
224
        help = "Specify the aggregator for combining element-wise similarity scores into a birthmark-wise similarity score.
225
Available: 
226
- hungarian  Use the Hungarian algorithm to find the optimal matching between elements of two birthmarks,
227
             maximizing the total similarity score.
228
- topn:N     For each element in the first birthmark, consider only the top N most similar elements in the
229
             second birthmark when calculating the overall similarity score. This can reduce noise from less
230
             relevant matches and focus on the most significant similarities."
231
    )]
232
    aggregator: Aggregator,
233

234
    #[clap(short, long, value_enum, default_value_t = PairingStrategy::AllAndSelf, value_name = "STRATEGY", ignore_case = true, help = "Specify the pairing strategy for comparing files.")]
235
    strategy: PairingStrategy,
236

237
    #[clap(short, long, value_name = "DIRECTORY", help = "Specify the destination directory for the comparing results", default_value = "similarities")]
238
    dest: PathBuf,
239

240
    #[clap(short = 'S', long, default_value_t = false, help = "Skip if the similarity file already exists for the pair of birthmarks")]
241
    skip: bool,
242

243
    #[clap(index = 1, value_name = "JSON_FILES", help = "Path to the birthmark JSON files to compare")]
244
    files: Vec<PathBuf>,
245
}
246

247
impl CompareOpts {
248
    pub fn dest(&self) -> &Path {
2✔
249
        &self.dest
2✔
250
    }
2✔
251

252
    pub fn comparator(&self) -> Comparator {
2✔
253
        self.algorithm.comparator()
2✔
254
    }
2✔
255

256
    pub fn iter(&self) -> Box<dyn Iterator<Item = (&PathBuf, &PathBuf)> + Send + '_> {
2✔
257
        self.strategy.pairs(&self.files)
2✔
258
    }
2✔
259

260
    pub fn aggregator(&self) -> &Aggregator {
2✔
261
        log::info!("Using {:?} as the aggregator for combining element-wise similarity scores", self.aggregator);
2✔
262
        &self.aggregator
2✔
263
    }
2✔
264

265
    pub fn compare_count(&self) -> usize {
2✔
266
        self.strategy.compare_count(&self.files)
2✔
267
    }
2✔
268

269
    pub fn is_skip(&self) -> bool {
2✔
270
        self.skip
2✔
271
    }
2✔
272
}
273

274
#[derive(Debug, clap::Parser)]
275
pub struct RunOpts {
276
    #[clap(short, long, value_enum, default_value_t = Analysis::OpSetJaccard, ignore_case = true, help = "Similarity algorithm to use")]
277
    pub(crate) analysis: Analysis,
278

279
    #[clap(short, long, value_enum, default_value_t = PairingStrategy::AllAndSelf, ignore_case = true, help = "Pairing strategy for file comparisons")]
280
    pub strategy: PairingStrategy,
281

282
    #[clap(short, long, default_value = "similarities", help = "Destination path for the output CSV file (default: 'similarities' directory")]
283
    pub(crate) dest: PathBuf,
284

285
    #[clap(
286
        short = 'A', long, default_value = "hungarian", value_name = "METHOD", ignore_case = true,
287
        help = "Specify the aggregator for combining element-wise similarity scores into a birthmark-wise similarity score.
288
Available: 
289
- hungarian  Use the Hungarian algorithm to find the optimal matching between elements of two birthmarks,
290
             maximizing the total similarity score.
291
- topn:N     For each element in the first birthmark, consider only the top N most similar elements in the
292
             second birthmark when calculating the overall similarity score. This can reduce noise from less
293
             relevant matches and focus on the most significant similarities. available topn:N or topn:all (same as topn)."
294
    )]
295
    aggregator: Aggregator,
296

297
    #[clap(short = 'S', long, default_value_t = false, help = "Skip if the similarity file already exists for the pair of birthmarks")]
298
    pub(crate) skip: bool,
299

300
    #[clap(index = 1, help = "Path to the JSON files")]
301
    pub(crate) files: Vec<PathBuf>,
302
}
303

304
impl RunOpts {
305
    pub fn dest(&self) -> &Path {
1✔
306
        &self.dest
1✔
307
    }
1✔
308

309
    pub fn analysis_type(&self) -> AnalysisType {
1✔
310
        (&self.analysis).into()
1✔
311
    }
1✔
312

313
    pub fn iter(&self) -> Box<dyn Iterator<Item = (&PathBuf, &PathBuf)> + Send + '_> {
1✔
314
        self.strategy.pairs(&self.files)
1✔
315
    }
1✔
316

317
    pub fn compare_count(&self) -> usize {
1✔
318
        self.strategy.compare_count(&self.files)
1✔
319
    }
1✔
320

321
    pub fn is_skip(&self) -> bool {
×
322
        self.skip
×
323
    }
×
324

325
    pub fn aggregator(&self) -> &Aggregator {
1✔
326
        log::info!("Using {:?} as the aggregator for combining element-wise similarity scores", self.aggregator);
1✔
327
        &self.aggregator
1✔
328
    }
1✔
329
}
330

331
#[derive(ValueEnum, Clone, Debug)]
332
#[clap(rename_all = "kebab-case")]
333
pub(crate) enum Analysis {
334
    FcFreqCosine,
335
    FcSetDice,
336
    FcFreqEuclidean,
337
    FcSetJaccard,
338
    FcSeqLevenshtein,
339
    FcSeqLcs,
340
    FcSetSimpson,
341
    FcFreqWeightedjaccard,
342

343
    OpFreqCosine,
344
    OpSetDice,
345
    OpFreqEuclidean,
346
    OpSetJaccard,
347
    OpSeqLevenshtein,
348
    OpSeqLcs,
349
    OpSetSimpson,
350
    OpFreqWeightedjaccard,
351

352
    Op1gramSetDice,
353
    Op1gramSetJaccard,
354
    Op1gramSetSimpson,
355
    Op1gramSeqLevenshtein,
356
    Op1gramSeqLcs,
357
    Op1gramFreqCosine,
358
    Op1gramFreqEuclidean,
359
    Op1gramFreqWeightedjaccard,
360

361
    Op2gramSetDice,
362
    Op2gramSetJaccard,
363
    Op2gramSetSimpson,
364
    Op2gramSeqLevenshtein,
365
    Op2gramSeqLcs,
366
    Op2gramFreqCosine,
367
    Op2gramFreqEuclidean,
368
    Op2gramFreqWeightedjaccard,
369

370
    Op3gramSetDice,
371
    Op3gramSetJaccard,
372
    Op3gramSetSimpson,
373
    Op3gramSeqLevenshtein,
374
    Op3gramSeqLcs,
375
    Op3gramFreqCosine,
376
    Op3gramFreqEuclidean,
377
    Op3gramFreqWeightedjaccard,
378

379
    Op4gramSetDice,
380
    Op4gramSetJaccard,
381
    Op4gramSetSimpson,
382
    Op4gramSeqLevenshtein,
383
    Op4gramSeqLcs,
384
    Op4gramFreqCosine,
385
    Op4gramFreqEuclidean,
386
    Op4gramFreqWeightedjaccard,
387

388
    Op5gramSetDice,
389
    Op5gramSetJaccard,
390
    Op5gramSetSimpson,
391
    Op5gramSeqLevenshtein,
392
    Op5gramSeqLcs,
393
    Op5gramFreqCosine,
394
    Op5gramFreqEuclidean,
395
    Op5gramFreqWeightedjaccard,
396

397
    Op6gramSetDice,
398
    Op6gramSetJaccard,
399
    Op6gramSetSimpson,
400
    Op6gramSeqLevenshtein,
401
    Op6gramSeqLcs,
402
    Op6gramFreqCosine,
403
    Op6gramFreqEuclidean,
404
    Op6gramFreqWeightedjaccard,
405
}
406

407
impl From<Analysis> for AnalysisType {
408
    fn from(v: Analysis) -> Self {
×
409
        AnalysisType::from(&v)
×
410
    }
×
411
}
412

413
impl From<&Analysis> for AnalysisType {
414
    fn from(value: &Analysis) -> Self {
1✔
415
        match value {
1✔
416
            Analysis::FcFreqCosine => AnalysisType::new(BirthmarkType::FcFreq, Algorithm::Cosine),
×
417
            Analysis::FcSetDice => AnalysisType::new(BirthmarkType::FcSet, Algorithm::Dice),
×
418
            Analysis::FcFreqEuclidean => AnalysisType::new(BirthmarkType::FcFreq, Algorithm::Euclidean),
×
419
            Analysis::FcSetJaccard => AnalysisType::new(BirthmarkType::FcSet, Algorithm::Jaccard),
×
420
            Analysis::FcSeqLevenshtein => AnalysisType::new(BirthmarkType::FcSeq, Algorithm::Levenshtein),
×
421
            Analysis::FcSeqLcs => AnalysisType::new(BirthmarkType::FcSeq, Algorithm::Lcs),
×
422
            Analysis::FcSetSimpson => AnalysisType::new(BirthmarkType::FcSet, Algorithm::Simpson),
×
423
            Analysis::FcFreqWeightedjaccard => AnalysisType::new(BirthmarkType::FcFreq, Algorithm::WeightedJaccard),
×
424

425
            Analysis::OpFreqCosine => AnalysisType::new(BirthmarkType::OpFreq, Algorithm::Cosine),
×
426
            Analysis::OpSetDice => AnalysisType::new(BirthmarkType::OpSet, Algorithm::Dice),
×
427
            Analysis::OpFreqEuclidean => AnalysisType::new(BirthmarkType::OpFreq, Algorithm::Euclidean),
×
428
            Analysis::OpSetJaccard => AnalysisType::new(BirthmarkType::OpSet, Algorithm::Jaccard),
1✔
429
            Analysis::OpSeqLevenshtein => AnalysisType::new(BirthmarkType::OpSeq, Algorithm::Levenshtein),
×
430
            Analysis::OpSeqLcs => AnalysisType::new(BirthmarkType::OpSeq, Algorithm::Lcs),
×
431
            Analysis::OpSetSimpson => AnalysisType::new(BirthmarkType::OpSet, Algorithm::Simpson),
×
432
            Analysis::OpFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpFreq, Algorithm::WeightedJaccard),
×
433

434
            Analysis::Op1gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(1), Algorithm::Dice),
×
435
            Analysis::Op1gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(1), Algorithm::Jaccard),
×
436
            Analysis::Op1gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(1), Algorithm::Simpson),
×
437
            Analysis::Op1gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(1), Algorithm::Levenshtein),
×
438
            Analysis::Op1gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(1), Algorithm::Lcs),
×
439
            Analysis::Op1gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(1), Algorithm::Cosine),
×
440
            Analysis::Op1gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(1), Algorithm::Euclidean),
×
441
            Analysis::Op1gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(1), Algorithm::WeightedJaccard),
×
442

443
            Analysis::Op2gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(2), Algorithm::Dice),
×
444
            Analysis::Op2gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(2), Algorithm::Jaccard),
×
445
            Analysis::Op2gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(2), Algorithm::Simpson),
×
446
            Analysis::Op2gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(2), Algorithm::Levenshtein),
×
447
            Analysis::Op2gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(2), Algorithm::Lcs),
×
448
            Analysis::Op2gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(2), Algorithm::Cosine),
×
449
            Analysis::Op2gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(2), Algorithm::Euclidean),
×
450
            Analysis::Op2gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(2), Algorithm::WeightedJaccard),
×
451

452
            Analysis::Op3gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(3), Algorithm::Dice),
×
453
            Analysis::Op3gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(3), Algorithm::Jaccard),
×
454
            Analysis::Op3gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(3), Algorithm::Simpson),
×
455
            Analysis::Op3gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(3), Algorithm::Levenshtein),
×
456
            Analysis::Op3gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(3), Algorithm::Lcs),
×
457
            Analysis::Op3gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(3), Algorithm::Cosine),
×
458
            Analysis::Op3gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(3), Algorithm::Euclidean),
×
459
            Analysis::Op3gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(3), Algorithm::WeightedJaccard),
×
460

461
            Analysis::Op4gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(4), Algorithm::Dice),
×
462
            Analysis::Op4gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(4), Algorithm::Jaccard),
×
463
            Analysis::Op4gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(4), Algorithm::Simpson),
×
464
            Analysis::Op4gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(4), Algorithm::Levenshtein),
×
465
            Analysis::Op4gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(4), Algorithm::Lcs),
×
466
            Analysis::Op4gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(4), Algorithm::Cosine),
×
467
            Analysis::Op4gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(4), Algorithm::Euclidean),
×
468
            Analysis::Op4gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(4), Algorithm::WeightedJaccard),
×
469

470
            Analysis::Op5gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(5), Algorithm::Dice),
×
471
            Analysis::Op5gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(5), Algorithm::Jaccard),
×
472
            Analysis::Op5gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(5), Algorithm::Simpson),
×
473
            Analysis::Op5gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(5), Algorithm::Levenshtein),
×
474
            Analysis::Op5gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(5), Algorithm::Lcs),
×
475
            Analysis::Op5gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(5), Algorithm::Cosine),
×
476
            Analysis::Op5gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(5), Algorithm::Euclidean),
×
477
            Analysis::Op5gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(5), Algorithm::WeightedJaccard),
×
478

479
            Analysis::Op6gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(6), Algorithm::Dice),
×
480
            Analysis::Op6gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(6), Algorithm::Jaccard),
×
481
            Analysis::Op6gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(6), Algorithm::Simpson),
×
482
            Analysis::Op6gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(6), Algorithm::Levenshtein),
×
483
            Analysis::Op6gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(6), Algorithm::Lcs),
×
484
            Analysis::Op6gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(6), Algorithm::Cosine),
×
485
            Analysis::Op6gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(6), Algorithm::Euclidean),
×
486
            Analysis::Op6gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(6), Algorithm::WeightedJaccard),
×
487

488
        }
489
    }
1✔
490
}
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