First commit
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# Maintain dependencies for Cargo
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
# Maintain dependencies for GitHub Actions
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
|
||||||
|
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
name: fmt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
- name: check formatting
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
- name: Cache Cargo dependencies
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
clippy:
|
||||||
|
name: clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
checks: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: clippy
|
||||||
|
- name: Run clippy action
|
||||||
|
uses: clechasseur/rs-clippy-check@v3
|
||||||
|
- name: Cache Cargo dependencies
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
doc:
|
||||||
|
# run docs generation on nightly rather than stable. This enables features like
|
||||||
|
# https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an
|
||||||
|
# API be documented as only available in some specific platforms.
|
||||||
|
name: doc
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Rust nightly
|
||||||
|
uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- name: Run cargo doc
|
||||||
|
run: cargo doc --no-deps --all-features
|
||||||
|
env:
|
||||||
|
RUSTDOCFLAGS: --cfg docsrs
|
||||||
|
test:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
name: test ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, windows-latest]
|
||||||
|
steps:
|
||||||
|
# if your project needs OpenSSL, uncomment this to fix Windows builds.
|
||||||
|
# it's commented out by default as the install command takes 5-10m.
|
||||||
|
# - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||||
|
# if: runner.os == 'Windows'
|
||||||
|
# - run: vcpkg install openssl:x64-windows-static-md
|
||||||
|
# if: runner.os == 'Windows'
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
# enable this ci template to run regardless of whether the lockfile is checked in or not
|
||||||
|
- name: cargo generate-lockfile
|
||||||
|
if: hashFiles('Cargo.lock') == ''
|
||||||
|
run: cargo generate-lockfile
|
||||||
|
- name: cargo test --locked
|
||||||
|
run: cargo test --locked --all-features --all-targets
|
||||||
|
- name: Cache Cargo dependencies
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "binocle"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A TUI to do binary computations"
|
||||||
|
authors = ["Julien Thillard <julien.thillard38@gmail.com>"]
|
||||||
|
license = "MIT"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
color-eyre = "0.6"
|
||||||
|
crossterm = "0.29"
|
||||||
|
ratatui = "0.30"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
opt-level = "s"
|
||||||
|
strip = true
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) supersurviveur <julien.thillard38@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# binocle
|
||||||
|
|
||||||
|
This is a [Ratatui] app generated by the [Hello World template].
|
||||||
|
|
||||||
|
[Ratatui]: https://ratatui.rs
|
||||||
|
[Hello World Template]: https://github.com/ratatui/templates/tree/main/hello-world
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright (c) supersurviveur <julien.thillard38@gmail.com>
|
||||||
|
|
||||||
|
This project is licensed under the MIT license ([LICENSE] or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
[LICENSE]: ./LICENSE
|
||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
use std::io;
|
||||||
|
|
||||||
|
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
||||||
|
use ratatui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
|
style::Stylize,
|
||||||
|
symbols::border,
|
||||||
|
text::{Line, Text},
|
||||||
|
widgets::{Block, Paragraph, Widget},
|
||||||
|
DefaultTerminal, Frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
binary_repr::BinaryRepr, hexadecimal::Hexadecimal, signed::Signed,
|
||||||
|
twos_complement::TwosComplement, unsigned::Unsigned,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct App {
|
||||||
|
exit: bool,
|
||||||
|
character_index: usize,
|
||||||
|
input: String,
|
||||||
|
|
||||||
|
bit_size: usize,
|
||||||
|
parsed_input: BinaryRepr,
|
||||||
|
|
||||||
|
// Components
|
||||||
|
twos_complement: TwosComplement,
|
||||||
|
hexadecimal: Hexadecimal,
|
||||||
|
unsigned: Unsigned,
|
||||||
|
signed: Signed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
|
||||||
|
self.bit_size = 32;
|
||||||
|
while !self.exit {
|
||||||
|
terminal.draw(|frame| self.draw(frame))?;
|
||||||
|
self.handle_events()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, frame: &mut Frame) {
|
||||||
|
frame.render_widget(self, frame.area());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_events(&mut self) -> io::Result<()> {
|
||||||
|
match event::read()? {
|
||||||
|
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
||||||
|
self.handle_key_event(key_event);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||||||
|
match key_event.code {
|
||||||
|
KeyCode::Char('q') => self.exit(),
|
||||||
|
// KeyCode::Left => self.decrement_counter(),
|
||||||
|
// KeyCode::Right => self.increment_counter(),
|
||||||
|
KeyCode::Char(to_insert) => self.enter_char(to_insert),
|
||||||
|
KeyCode::Backspace => self.delete_char(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match key_event.code {
|
||||||
|
KeyCode::Char(_) | KeyCode::Backspace => self.update(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_char(&mut self, input: char) {
|
||||||
|
if input.is_ascii_hexdigit()
|
||||||
|
|| ((input == 'x' || input == 'o')
|
||||||
|
&& self.character_index == 1
|
||||||
|
&& self.input.starts_with('0'))
|
||||||
|
{
|
||||||
|
self.input.push(input);
|
||||||
|
self.character_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_char(&mut self) {
|
||||||
|
let is_not_cursor_leftmost = self.character_index != 0;
|
||||||
|
if is_not_cursor_leftmost {
|
||||||
|
self.character_index -= 1;
|
||||||
|
self.input.remove(self.character_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self) {
|
||||||
|
// TODO manage parsing errors
|
||||||
|
let (input, base) = if self.input.starts_with("0x") {
|
||||||
|
(&self.input[2..], 16)
|
||||||
|
} else if self.input.starts_with("0o") {
|
||||||
|
(&self.input[2..], 8)
|
||||||
|
} else if self.input.starts_with("0b") {
|
||||||
|
(&self.input[2..], 2)
|
||||||
|
} else {
|
||||||
|
(self.input.as_str(), 10)
|
||||||
|
};
|
||||||
|
if let Ok(parsed) = u128::from_str_radix(input, base) {
|
||||||
|
self.parsed_input = BinaryRepr::from_u128(parsed, self.bit_size);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.twos_complement.update(&self.parsed_input);
|
||||||
|
self.hexadecimal.update(&self.parsed_input);
|
||||||
|
self.unsigned.update(&self.parsed_input);
|
||||||
|
self.signed.update(&self.parsed_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn exit(&mut self) {
|
||||||
|
self.exit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &App {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let layout = Layout::new(
|
||||||
|
Direction::Vertical,
|
||||||
|
[Constraint::Fill(1), Constraint::Length(3)],
|
||||||
|
);
|
||||||
|
let [output_area, input_area] = area.layout(&layout);
|
||||||
|
|
||||||
|
let output_layout = Layout::new(
|
||||||
|
Direction::Vertical,
|
||||||
|
[
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let [twos_complement_area, hexadecimal_area, unsigned_area, signed_area] =
|
||||||
|
output_area.layout(&output_layout);
|
||||||
|
|
||||||
|
self.twos_complement.render(twos_complement_area, buf);
|
||||||
|
self.hexadecimal.render(hexadecimal_area, buf);
|
||||||
|
self.unsigned.render(unsigned_area, buf);
|
||||||
|
self.signed.render(signed_area, buf);
|
||||||
|
|
||||||
|
let input_layout = Layout::new(
|
||||||
|
Direction::Horizontal,
|
||||||
|
[Constraint::Fill(1), Constraint::Length(10)],
|
||||||
|
);
|
||||||
|
let [input_area, size_area] = input_area.layout(&input_layout);
|
||||||
|
|
||||||
|
let input_text = Text::from(self.input.clone().yellow());
|
||||||
|
|
||||||
|
let input_block = Block::bordered()
|
||||||
|
.title(Line::from("input".bold()))
|
||||||
|
.border_set(border::PLAIN);
|
||||||
|
|
||||||
|
Paragraph::new(input_text)
|
||||||
|
.block(input_block)
|
||||||
|
.render(input_area, buf);
|
||||||
|
|
||||||
|
let size_text = Text::from(self.bit_size.yellow());
|
||||||
|
|
||||||
|
let size_block = Block::bordered()
|
||||||
|
.title(Line::from("size".bold()))
|
||||||
|
.border_set(border::PLAIN);
|
||||||
|
|
||||||
|
Paragraph::new(size_text)
|
||||||
|
.block(size_block)
|
||||||
|
.render(size_area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
use std::ops::{Add, Not, Sub};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct BinaryRepr {
|
||||||
|
pub bits: Vec<bool>,
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinaryRepr {
|
||||||
|
pub const fn new(bits: Vec<bool>, size: usize) -> Self {
|
||||||
|
Self { bits, size }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_hex(&self) -> String {
|
||||||
|
let mut hex = String::new();
|
||||||
|
|
||||||
|
for chunk in self.bits.chunks(4) {
|
||||||
|
let nibble = chunk
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.fold(0, |acc, (i, bit)| acc + (u8::from(*bit) << i));
|
||||||
|
|
||||||
|
hex.insert(
|
||||||
|
0,
|
||||||
|
[
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||||
|
][nibble as usize],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hex
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_unsigned(&self) -> String {
|
||||||
|
let mut digits: Vec<u8> = vec![0];
|
||||||
|
|
||||||
|
for bit in self.bits.iter().rev() {
|
||||||
|
let mut carry = u8::from(*bit);
|
||||||
|
for digit in &mut digits {
|
||||||
|
let value = *digit * 2 + carry;
|
||||||
|
*digit = value % 10;
|
||||||
|
carry = value / 10;
|
||||||
|
}
|
||||||
|
while carry > 0 {
|
||||||
|
digits.push(carry % 10);
|
||||||
|
carry /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while digits.len() > 1 && *digits.last().unwrap() == 0 {
|
||||||
|
digits.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
digits
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|digit| (digit + b'0') as char)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_signed(&self) -> String {
|
||||||
|
let mut digits: Vec<u8> = vec![0];
|
||||||
|
|
||||||
|
for bit in self.bits.iter().rev().skip(1) {
|
||||||
|
let mut carry = u8::from(*bit);
|
||||||
|
for digit in &mut digits {
|
||||||
|
let value = *digit * 2 + carry;
|
||||||
|
*digit = value % 10;
|
||||||
|
carry = value / 10;
|
||||||
|
}
|
||||||
|
while carry > 0 {
|
||||||
|
digits.push(carry % 10);
|
||||||
|
carry /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while digits.len() > 1 && *digits.last().unwrap() == 0 {
|
||||||
|
digits.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.size != 0 && self.bits[self.size - 1] {
|
||||||
|
"-"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
.to_owned()
|
||||||
|
+ &digits
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|digit| (digit + b'0') as char)
|
||||||
|
.collect::<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_u128(mut value: u128, size: usize) -> Self {
|
||||||
|
Self::new(
|
||||||
|
(0..size)
|
||||||
|
.map(|_| {
|
||||||
|
let out = value & 1 != 0;
|
||||||
|
value >>= 1;
|
||||||
|
out
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not for BinaryRepr {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
Self::new(self.bits.iter().map(|a| !a).collect(), self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not for &BinaryRepr {
|
||||||
|
type Output = BinaryRepr;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
!self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<&Self> for BinaryRepr {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: &Self) -> Self::Output {
|
||||||
|
assert_eq!(self.size, rhs.size);
|
||||||
|
|
||||||
|
let mut carry = false;
|
||||||
|
|
||||||
|
self.bits
|
||||||
|
.iter_mut()
|
||||||
|
.zip(rhs.bits.iter())
|
||||||
|
.for_each(|(a, b)| {
|
||||||
|
let half = *a ^ b;
|
||||||
|
let out = half ^ carry;
|
||||||
|
carry = (*a & b) || (half & carry);
|
||||||
|
*a = out;
|
||||||
|
});
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for BinaryRepr {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
self + &rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<&Self> for BinaryRepr {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: &Self) -> Self::Output {
|
||||||
|
assert_eq!(self.size, rhs.size);
|
||||||
|
|
||||||
|
let size = self.size;
|
||||||
|
|
||||||
|
self + &!rhs + Self::from_u128(1, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinaryRepr {
|
||||||
|
pub fn twos_complement(&self) -> Self {
|
||||||
|
!self + Self::from_u128(1, self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use ratatui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::Rect,
|
||||||
|
style::Stylize,
|
||||||
|
symbols::border,
|
||||||
|
text::{Line, Text},
|
||||||
|
widgets::{Block, Paragraph, Widget},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::binary_repr::BinaryRepr;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Hexadecimal {
|
||||||
|
repr: BinaryRepr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hexadecimal {
|
||||||
|
pub fn update(&mut self, input: &BinaryRepr) {
|
||||||
|
self.repr = input.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &Hexadecimal {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let input_text = Text::from(("0x".to_owned() + &self.repr.to_hex()).yellow());
|
||||||
|
|
||||||
|
let input_block = Block::bordered()
|
||||||
|
.title(Line::from("hexadecimal".bold()))
|
||||||
|
.border_set(border::PLAIN);
|
||||||
|
|
||||||
|
Paragraph::new(input_text)
|
||||||
|
.block(input_block)
|
||||||
|
.render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#![deny(clippy::nursery, clippy::pedantic)]
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
|
||||||
|
mod app;
|
||||||
|
mod binary_repr;
|
||||||
|
mod hexadecimal;
|
||||||
|
mod twos_complement;
|
||||||
|
mod unsigned;
|
||||||
|
mod signed;
|
||||||
|
|
||||||
|
fn main() -> color_eyre::Result<()> {
|
||||||
|
color_eyre::install()?;
|
||||||
|
ratatui::run(|terminal| App::default().run(terminal))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use ratatui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::Rect,
|
||||||
|
style::Stylize,
|
||||||
|
symbols::border,
|
||||||
|
text::{Line, Text},
|
||||||
|
widgets::{Block, Paragraph, Widget},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::binary_repr::BinaryRepr;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Signed {
|
||||||
|
repr: BinaryRepr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signed {
|
||||||
|
pub fn update(&mut self, input: &BinaryRepr) {
|
||||||
|
self.repr = input.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &Signed {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let input_text = Text::from(self.repr.to_signed().yellow());
|
||||||
|
|
||||||
|
let input_block = Block::bordered()
|
||||||
|
.title(Line::from("signed".bold()))
|
||||||
|
.border_set(border::PLAIN);
|
||||||
|
|
||||||
|
Paragraph::new(input_text)
|
||||||
|
.block(input_block)
|
||||||
|
.render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use ratatui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::Rect,
|
||||||
|
style::Stylize,
|
||||||
|
symbols::border,
|
||||||
|
text::{Line, Text},
|
||||||
|
widgets::{Block, Paragraph, Widget},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::binary_repr::BinaryRepr;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TwosComplement {
|
||||||
|
repr: BinaryRepr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TwosComplement {
|
||||||
|
pub fn update(&mut self, input: &BinaryRepr) {
|
||||||
|
self.repr = input.twos_complement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &TwosComplement {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let input_text = Text::from(("0x".to_owned() + &self.repr.to_hex()).yellow());
|
||||||
|
|
||||||
|
let input_block = Block::bordered()
|
||||||
|
.title(Line::from("2's complement".bold()))
|
||||||
|
.border_set(border::PLAIN);
|
||||||
|
|
||||||
|
Paragraph::new(input_text)
|
||||||
|
.block(input_block)
|
||||||
|
.render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use ratatui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::Rect,
|
||||||
|
style::Stylize,
|
||||||
|
symbols::border,
|
||||||
|
text::{Line, Text},
|
||||||
|
widgets::{Block, Paragraph, Widget},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::binary_repr::BinaryRepr;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Unsigned {
|
||||||
|
repr: BinaryRepr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unsigned {
|
||||||
|
pub fn update(&mut self, input: &BinaryRepr) {
|
||||||
|
self.repr = input.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for &Unsigned {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let input_text = Text::from(self.repr.to_unsigned().yellow());
|
||||||
|
|
||||||
|
let input_block = Block::bordered()
|
||||||
|
.title(Line::from("unsigned".bold()))
|
||||||
|
.border_set(border::PLAIN);
|
||||||
|
|
||||||
|
Paragraph::new(input_text)
|
||||||
|
.block(input_block)
|
||||||
|
.render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user