Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/cairo/scripts/url_parser/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scarb 2.11.3
6 changes: 6 additions & 0 deletions examples/cairo/scripts/url_parser/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "url_parser"
version = "0.1.0"
13 changes: 13 additions & 0 deletions examples/cairo/scripts/url_parser/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "url_parser"
version = "0.1.0"
edition = "2023_01"

[dependencies]
starknet = ">=2.3.1"

[dev-dependencies]
cairo_test = "2.3.1"

[[target.starknet-contract]]
crate-type = ["lib"]
123 changes: 123 additions & 0 deletions examples/cairo/scripts/url_parser/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use core::array::ArrayTrait;
use core::option::OptionTrait;
use core::traits::Into;
use core::clone::Clone;

mod string_utils;

#[derive(Drop, Clone)]
pub struct URL {
protocol: felt252,
domain: felt252,
path: felt252,
query: felt252,
fragment: felt252,
}

pub trait URLParserTrait {
fn parse_url(url: felt252) -> URL;
fn extract_protocol(url: felt252) -> felt252;
fn extract_domain(url: felt252) -> felt252;
fn extract_path(url: felt252) -> felt252;
fn extract_query(url: felt252) -> felt252;
fn extract_fragment(url: felt252) -> felt252;
}

impl URLParser of URLParserTrait {
fn parse_url(url: felt252) -> URL {
URL {
protocol: Self::extract_protocol(url),
domain: Self::extract_domain(url),
path: Self::extract_path(url),
query: Self::extract_query(url),
fragment: Self::extract_fragment(url)
}
}

fn extract_protocol(url: felt252) -> felt252 {
let (protocol, remaining) = string_utils::split_string(url, '://');
if remaining == 0 {
return 'http';
}
protocol
}

fn extract_domain(url: felt252) -> felt252 {
let (_, after_protocol) = string_utils::split_string(url, '://');
if after_protocol == 0 {
// No protocol found, treat entire URL as domain until first slash
let (domain, _) = string_utils::split_string(url, '/');
return domain;
}

// Extract domain from the remaining URL
let (domain, _) = string_utils::split_string(after_protocol, '/');
if domain == 0 {
return after_protocol;
}

// Remove query string if present
let (domain_no_query, _) = string_utils::split_string(domain, '?');
if domain_no_query == 0 {
return domain;
}

// Remove fragment if present
let (final_domain, _) = string_utils::split_string(domain_no_query, '#');
if final_domain == 0 {
return domain_no_query;
}

final_domain
}

fn extract_path(url: felt252) -> felt252 {
// First split on protocol
let (_, after_protocol) = string_utils::split_string(url, '://');
let url_to_check = if after_protocol == 0 { url } else { after_protocol };

// Split by '/' first
let (_, after_slash) = string_utils::split_string(url_to_check, '/');
if after_slash == 0 {
return '/';
}

// Get just the path part (before ? or #)
let mut path = after_slash;

// Remove query part if exists
let (before_query, _) = string_utils::split_string(path, '?');
if before_query != 0 {
path = before_query;
}

// Remove fragment part if exists
let (before_fragment, _) = string_utils::split_string(path, '#');
if before_fragment != 0 {
path = before_fragment;
}

// Return with leading slash
if path == 0 {
return '/';
}

let path_with_slash = '/';
path_with_slash + path
}

fn extract_query(url: felt252) -> felt252 {
let (_, after_query) = string_utils::split_string(url, '?');
if after_query == 0 {
return 0;
}

let (query, _) = string_utils::split_string(after_query, '#');
query
}

fn extract_fragment(url: felt252) -> felt252 {
let (_, fragment) = string_utils::split_string(url, '#');
fragment
}
}
151 changes: 151 additions & 0 deletions examples/cairo/scripts/url_parser/src/string_utils.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use core::array::ArrayTrait;
use core::option::OptionTrait;
use core::traits::{Into, TryInto};
use core::integer::{u256_from_felt252, U256TryIntoFelt252};

fn string_to_array(str: felt252) -> Array<felt252> {
let mut result = ArrayTrait::new();

if str == 0 {
return result;
}

// Convert the string to bytes in reverse order
let mut chars = ArrayTrait::new();
let mut remaining = str;

loop {
// Convert to u256 for division
let remaining_u256 = u256_from_felt252(remaining);
let div_u256 = u256_from_felt252(256);

// Perform division and get remainder
let quotient = remaining_u256 / div_u256;
let remainder = remaining_u256 - (quotient * div_u256);

// Convert back to felt252
let char: felt252 = remainder.try_into().unwrap();
chars.append(char);
remaining = quotient.try_into().unwrap();

if remaining == 0 {
break;
}
};

// Reverse the array to get correct order
let mut i = chars.len();
loop {
if i == 0 {
break;
}
i -= 1;
result.append(*chars.at(i));
};

result
}

fn find_substring(str: felt252, substr: felt252) -> u32 {
if str == 0 || substr == 0 {
return 0;
}

let str_arr = string_to_array(str);
let substr_arr = string_to_array(substr);

if substr_arr.len() == 0 || str_arr.len() < substr_arr.len() {
return 0;
}

let mut i: u32 = 0;
let max_i = str_arr.len() - substr_arr.len();

loop {
if i >= max_i.try_into().unwrap() {
break;
}

let mut found = true;
let mut j: u32 = 0;
let max_j = substr_arr.len().try_into().unwrap();

loop {
if j >= max_j {
break;
}

let str_char = *str_arr.at(i.try_into().unwrap() + j.try_into().unwrap());
let substr_char = *substr_arr.at(j.try_into().unwrap());

if str_char != substr_char {
found = false;
break;
}
j += 1;
};

if found {
return i;
}
i += 1;
};

0
}

fn split_string(str: felt252, delimiter: felt252) -> (felt252, felt252) {
if str == 0 || delimiter == 0 {
return (str, 0);
}

let str_arr = string_to_array(str);
let delimiter_arr = string_to_array(delimiter);

let pos = find_substring(str, delimiter);
if pos == 0 {
return (str, 0);
}

let mut first = ArrayTrait::new();
let mut second = ArrayTrait::new();

let mut i: u32 = 0;
let max_i = str_arr.len().try_into().unwrap();
let delimiter_len: u32 = delimiter_arr.len().try_into().unwrap();

loop {
if i >= max_i {
break;
}

if i < pos {
first.append(*str_arr.at(i.try_into().unwrap()));
} else if i >= pos + delimiter_len {
second.append(*str_arr.at(i.try_into().unwrap()));
}

i += 1;
};

(array_to_string(first), array_to_string(second))
}

fn array_to_string(arr: Array<felt252>) -> felt252 {
if arr.len() == 0 {
return 0;
}

let mut result: felt252 = 0;
let mut i = 0_usize;

loop {
if i >= arr.len() {
break;
}
result = result * 256 + *arr.at(i);
i += 1;
};

result
}
1 change: 1 addition & 0 deletions examples/cairo/scripts/url_parser/tests/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod test_url_parser;
Loading