Skip to content

Commit

Permalink
Merge pull request #60 from TheBestTvarynka/diff-checker
Browse files Browse the repository at this point in the history
Diff checker: Initial implementation
  • Loading branch information
TheBestTvarynka authored Mar 8, 2024
2 parents 0486f5e + 0b5a455 commit 165d1e0
Show file tree
Hide file tree
Showing 11 changed files with 426 additions and 7 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ log = "0.4.17"
# utils
hex = "0.4.3"
gloo-timers = "0.2.4"
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
rand_chacha = "0.3.1"
serde = { version = "1.0.162", features = ["derive"] }
serde_qs = "0.12.0"
serde_json = "1.0.89"
Expand All @@ -50,8 +48,13 @@ hmac-sha512 = { version = "1.1.2", features = ["sha384"] }
rsa = "0.7.2"
bcrypt = "0.14.0"
flate2 = { version = "1.0.26", features = ["zlib"] }
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
rand_chacha = "0.3.1"

# asn1
asn1-parser = { path = "./crates/asn1-parser", features = ["std"] }
oid = { version = "0.2.1", default-features = false }
paste = "1.0.14"

# diff
similar = "2.4.0"
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@
<link data-trunk rel="sass" href="public/styles/asn1/page.scss" />
<link data-trunk rel="sass" href="public/styles/asn1/node.scss" />
<link data-trunk rel="sass" href="public/styles/asn1/hex_viewer.scss" />

<!-- diff styles -->
<link data-trunk rel="sass" href="public/styles/diff/styles.scss" />
</head>
</html>
36 changes: 36 additions & 0 deletions public/styles/diff/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

.diff-changes-container {
display: flex;
flex-direction: row;
gap: 0;
justify-content: flex-start;
align-items: center;
width: 100%;
flex-wrap: wrap;
//background-color: #edd5ce;
//border-radius: 0.2em;
//padding: 0.5em;
}

.diff-insert {
white-space: pre-wrap;
background-color: #97e0bd;
color: #016939;
}

.diff-remove {
white-space: pre-wrap;
background-color: #d38693;
color: #9d0620;
}

.diff-new-line {
height: 0;
flex-basis: 100%;
}

.diff-line-number {
background-color: #edd5ce;
padding-right: 0.3em;
padding-left: 0.3em;
}
2 changes: 2 additions & 0 deletions src/about.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub fn about() -> Html {
<li>{"ES384"}</li>
<li>{"ES512"}</li>
</ul>
<li>{"ASN1 decoder"}</li>
<li>{"Diff checker"}</li>
<li>{"Ability to share the sample by url"}</li>
</ul>
<span>{"All computations are performed on the client side."}</span>
Expand Down
7 changes: 2 additions & 5 deletions src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ pub fn asn1_parser_page() -> Html {
let asn1_setter = parsed_asn1.setter();
let raw_data = (*raw_asn1).clone();
let parse_asn1 = Callback::from(move |_| match Asn1::decode_buff(&raw_data) {
Ok(asn1) => {
debug!("parsed!");
asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned()));
}
Ok(asn1) => asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned())),
Err(error) => notifications.spawn(Notification::new(
NotificationType::Error,
"Invalid asn1 data",
Expand Down Expand Up @@ -197,7 +194,7 @@ pub fn asn1_parser_page() -> Html {
</span>
<ByteInput bytes={(*raw_asn1).clone()} setter={Callback::from(move |data| raw_asn1_setter.set(data))} placeholder={"asn1 data".to_owned()} rows={10} />
<div class="horizontal">
<button class="action-button" {onclick}>{"Process"}</button>
<button class="action-button" {onclick}>{"Decode"}</button>
<span class="total">{"(ctrl+enter)"}</span>
<button class="button-with-icon" onclick={share_by_link}>
<img src="/public/img/icons/share_by_link.png" />
Expand Down
216 changes: 216 additions & 0 deletions src/diff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
mod diff_algo;
mod diff_viewer;

use similar::{capture_diff_slices, Algorithm, DiffOp, TextDiff};
use web_sys::{HtmlInputElement, KeyboardEvent};
use yew::html::onchange::Event;
use yew::virtual_dom::VNode;
use yew::{classes, function_component, html, use_effect_with_deps, use_state, Callback, Html, TargetCast};
use yew_hooks::use_local_storage;

use self::diff_algo::DiffAlgo;
use self::diff_viewer::DiffViewer;

const DEFAULT_ORIGINAL: &str = "TheBestTvarynka
TheBestTvarynka
TheBestTvarynka";
const DEFAULT_CHANGED: &str = "thebesttravynka
thebesttravynka
thebesttravynka
";
const DEFAULT_ALGORITHM: DiffAlgo = DiffAlgo(Algorithm::Myers);

const LOCAL_STORAGE_ORIGINAL: &str = "ORIGINAL_DATA";
const LOCAL_STORAGE_ALGORITHM: &str = "ALGORITHM";
const LOCAL_STORAGE_CHANGED: &str = "CHANGED_DATA";

const ALL_ALGORITHMS: &[DiffAlgo] = &[
DiffAlgo(Algorithm::Myers),
DiffAlgo(Algorithm::Lcs),
DiffAlgo(Algorithm::Patience),
];

#[derive(Debug, Clone, PartialEq)]
struct DiffData {
pub original: Vec<char>,
pub changed: Vec<char>,
pub changes: Vec<DiffOp>,
}

impl DiffData {
pub fn empty() -> Self {
Self {
original: Vec::new(),
changed: Vec::new(),
changes: Vec::new(),
}
}
}

fn render_algorithm_options(current_algorithm: DiffAlgo) -> Vec<VNode> {
ALL_ALGORITHMS
.iter()
.map(|algo| {
html! {
<option selected={current_algorithm == *algo} value={algo.to_string()}>{algo}</option>
}
})
.collect()
}

#[function_component(DiffPage)]
pub fn diff_page() -> Html {
let original = use_state(|| DEFAULT_ORIGINAL.to_owned());
let changed = use_state(|| DEFAULT_CHANGED.to_owned());
let algorithm = use_state(|| DEFAULT_ALGORITHM);
let diffs = use_state(|| {
let original = DEFAULT_ORIGINAL.chars().collect::<Vec<_>>();
let changed = DEFAULT_CHANGED.chars().collect::<Vec<_>>();
let changes = TextDiff::configure()
.algorithm(DEFAULT_ALGORITHM.into())
.newline_terminated(true)
.diff_chars(DEFAULT_ORIGINAL, DEFAULT_CHANGED);

DiffData {
original,
changed,
changes: changes.ops().to_owned(),
}
});

let original_data = original.chars().collect::<Vec<_>>();
let changed_data = changed.chars().collect::<Vec<_>>();
let diffs_setter = diffs.setter();
let algo = *algorithm;
let compute_diff = Callback::from(move |_: ()| {
let changes = capture_diff_slices(algo.into(), &original_data, &changed_data);

diffs_setter.set(DiffData {
original: original_data.clone(),
changed: changed_data.clone(),
changes,
});
});

let original_local_storage = use_local_storage::<String>(LOCAL_STORAGE_ORIGINAL.to_owned());
let original_setter = original.setter();
let changed_local_storage = use_local_storage::<String>(LOCAL_STORAGE_CHANGED.to_owned());
let changed_setter = changed.setter();
let algorithm_local_storage = use_local_storage::<String>(LOCAL_STORAGE_ALGORITHM.to_owned());
let algorithm_setter = algorithm.setter();
let diffs_setter = diffs.setter();
use_effect_with_deps(
move |_: &[(); 0]| {
let mut flag = false;

if let Some(original) = (*original_local_storage).as_ref() {
original_setter.set(original.to_string());
flag = true;
}
if let Some(changed) = (*changed_local_storage).as_ref() {
changed_setter.set(changed.to_string());
flag = true;
}
if let Some(raw_algorithm) = (*algorithm_local_storage).as_ref() {
if let Ok(algorithm) = raw_algorithm.as_str().try_into() {
algorithm_setter.set(algorithm);
flag = true;
}
}

if flag {
diffs_setter.set(DiffData::empty());
}
},
[],
);

let local_storage = use_local_storage::<String>(LOCAL_STORAGE_ORIGINAL.to_owned());
use_effect_with_deps(
move |[original]| {
local_storage.set((*original).to_string());
},
[original.clone()],
);

let local_storage = use_local_storage::<String>(LOCAL_STORAGE_CHANGED.to_owned());
use_effect_with_deps(
move |[changed]| {
local_storage.set((*changed).to_string());
},
[changed.clone()],
);

let local_storage = use_local_storage::<String>(LOCAL_STORAGE_ALGORITHM.to_owned());
use_effect_with_deps(
move |[algorithm]| {
local_storage.set((*algorithm).to_string());
},
[algorithm.clone()],
);

let original_setter = original.setter();
let on_original_input = Callback::from(move |event: html::oninput::Event| {
let input: HtmlInputElement = event.target_unchecked_into();
original_setter.set(input.value());
});

let changed_setter = changed.setter();
let on_changed_input = Callback::from(move |event: html::oninput::Event| {
let input: HtmlInputElement = event.target_unchecked_into();
changed_setter.set(input.value());
});

let diff = compute_diff.clone();
let onclick = Callback::from(move |_| {
diff.emit(());
});

let algorithm_setter = algorithm.setter();
let on_algorithm_change = Callback::from(move |event: Event| {
let input: HtmlInputElement = event.target_unchecked_into();
if let Ok(algorithm) = input.value().as_str().try_into() {
algorithm_setter.set(algorithm);
}
});

let onkeydown = Callback::from(move |event: KeyboardEvent| {
if event.ctrl_key() && event.code() == "Enter" {
compute_diff.emit(());
}
});

html! {
<div class={classes!("vertical", "asn1-page")} {onkeydown}>
<div class="horizontal">
<span>{"Diff algorithm:"}</span>
<div>
<select class="base-input" onchange={on_algorithm_change}>
{render_algorithm_options(*algorithm)}
</select>
</div>
</div>
<div class="horizontal">
<textarea
rows="8"
placeholder={"original"}
class="base-input"
value={(*original).clone()}
oninput={on_original_input}
/>
<textarea
rows="8"
placeholder={"changed"}
class="base-input"
value={(*changed).clone()}
oninput={on_changed_input}
/>
</div>
<div class="horizontal">
<button class="action-button" onclick={onclick}>{"Diff"}</button>
<span class="total">{"(ctrl+enter)"}</span>
</div>
<DiffViewer diff={(*diffs).clone()} />
</div>
}
}
54 changes: 54 additions & 0 deletions src/diff/diff_algo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::fmt::{Display, Formatter};
use std::ops::Deref;

use similar::Algorithm;

const MYERS: &str = "Myers";
const PATIENCE: &str = "Patience";
const LCS: &str = "Lcs";

#[derive(Clone, Copy, Eq, PartialEq)]
pub struct DiffAlgo(pub Algorithm);

impl From<DiffAlgo> for Algorithm {
fn from(value: DiffAlgo) -> Self {
value.0
}
}

impl From<Algorithm> for DiffAlgo {
fn from(value: Algorithm) -> Self {
Self(value)
}
}

impl Display for DiffAlgo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self.0 {
Algorithm::Myers => MYERS,
Algorithm::Patience => PATIENCE,
Algorithm::Lcs => LCS,
})
}
}

impl Deref for DiffAlgo {
type Target = Algorithm;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl TryFrom<&str> for DiffAlgo {
type Error = String;

fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(match value {
MYERS => Algorithm::Myers.into(),
PATIENCE => Algorithm::Patience.into(),
LCS => Algorithm::Lcs.into(),
_ => return Err(format!("Unsupported diff algorithm: {}.", value)),
})
}
}
Loading

0 comments on commit 165d1e0

Please sign in to comment.